//Diffuse BRDF //Project: LightWhat - CPU Pathtracer //https://github.com/Illation/LightWhat //Author: Robert Lindner //1. Texture data is extracted //2a) if not max bounces // Get a random vector in a cosine weighted hemisphere // Recursively raycast with that random vector (pathtracer bounces) //2b) if max bounces are reached // apply diffuse lambert shading from all lights // calculate light falloff from those lights //3. Return the color //I wrote a lot of this code before knowing a lot about c++ coding standards or the standard library //A lot of this could be more efficient and better written //generate tangent and bitangent for hemishphere sampling inline void tangentBitangent(vec3 N, vec3 &T, vec3 &B) { vec3 NU; if (N.x || N.z)NU = vec3(0, 1, 0);//non parallel up vector else NU = vec3(0, 0, 1); T = N.Cross(NU).Norm(); B = N.Cross(T).Norm(); } //cosine weighted unit disc inline void toUnitDisc(float &rx, float &ay) { const float r = sqrtf(rx);//get a radius const float phi = ay * PI2;//get an angle rx = r * cosf(phi); ay = r * sinf(phi); } //generate a random vector in a hemisphere around N where it is more probably to return a vector close to n inline vec3 cosineSampleHemisphere(vec3 N, vec3 T, vec3 B) { std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<> dis(0, 1); float u1 = (float)dis(gen); float u2 = (float)dis(gen); toUnitDisc(u1, u2); float costheta = sqrtf(max(1.0f - u1 * u1 - u2 * u2, 0.0f)); return (T*u1 + N*costheta + B*u2); } //Diffuse BRDF colRGB DiffuseBRDF::shade(DifferentialGeometry dg, LWScene *lScPtr, TraceUnit *lRenPtr) { //Header colRGB ret = colRGB(0,0,0); colRGB dif = diffuse; if (hasTexture)//get diffuse texture { dif = lScPtr->textures[TexIdx].getRGB(dg.uv.x, dg.uv.y); } vec3 N = dg.n; //tangent space normal mapping if (hasNormTex && dg.hasTangentSpace) { colRGB texCol = lScPtr->textures[normTexIdx].getRGB(dg.uv.x, dg.uv.y); vec3 texN; texN.x = (texCol.red * 2) - 1; texN.y = (texCol.green * 2) - 1; texN.z = (texCol.blue * 2) - 1; vec3 tSpaceN = dg.t*texN.x + dg.b*texN.y + N*texN.z; N = tSpaceN.Norm(); } if (N.Dot(dg.dir) > 0) { N = -N; } //Actual Shader if (dg.bounces > 0) { //generate cosine weighted hemisphere vector to surface normal vec3 T, B; if (dg.hasTangentSpace){T = dg.t, B = dg.b;}//get tangent space from differential geometry else MonteCarlo::tangentBitangent(N, T, B);//if no tangentspace exists, generate it vec3 R = MonteCarlo::cosineSampleHemisphere(N, T, B);//get a random ray within the cosine weighted hemisphere of the noraml vector //Recursivly Bounce Ray ray = Ray(line(dg.i.p, R), dg.bounces - 1, false); ray.precalculate(); float t; colRGB rcol = lRenPtr->raycast(ray, t);//get color from recursive raycast ret = rcol*dif*intensity; } else //calculate direct illumination { for (size_t i = 0; i < lScPtr->lights.size(); i++)//for all lights { //L inncoming light direction vec3 L = (lScPtr->lights[i]->getPosition() - dg.i.p); float distance = L.Length(); L /= distance; float LightIntensity = lRenPtr->getLightIntensity(lScPtr->lights[i], dg.i.p); //calculate light falloff if (LightIntensity>0) { float r = 1.f; float d = max(distance - r, 0); float denom = d / r + 1; float attenuation = 1 / (denom*denom); //Calculate Lambert diffuse float intensity = N.Dot(L) * LightIntensity * attenuation; if (intensity > 0.f) { ret += dif*lScPtr->lights[i]->getColor()*intensity; } } } } return ret; }