void Lighter::Light(float colorOut0[4], float colorOut1[4], const float colorIn[4], Vec3f pos, Vec3f norm) { Color4 in(colorIn); const Color4 *ambient; if (materialUpdate_ & 1) ambient = ∈ else ambient = &materialAmbient; const Color4 *diffuse; if (materialUpdate_ & 2) diffuse = ∈ else diffuse = &materialDiffuse; const Color4 *specular; if (materialUpdate_ & 4) specular = ∈ else specular = &materialSpecular; Color4 lightSum0 = globalAmbient * *ambient + materialEmissive; Color4 lightSum1(0, 0, 0, 0); for (int l = 0; l < 4; l++) { // can we skip this light? if (!gstate.isLightChanEnabled(l)) continue; GELightType type = gstate.getLightType(l); Vec3f toLight(0,0,0); Vec3f lightDir(0,0,0); if (type == GE_LIGHTTYPE_DIRECTIONAL) toLight = Vec3f(gstate_c.lightpos[l]); // lightdir is for spotlights else toLight = Vec3f(gstate_c.lightpos[l]) - pos; bool doSpecular = gstate.isUsingSpecularLight(l); bool poweredDiffuse = gstate.isUsingPoweredDiffuseLight(l); float distanceToLight = toLight.Length(); float dot = 0.0f; float angle = 0.0f; float lightScale = 0.0f; if (distanceToLight > 0.0f) { toLight /= distanceToLight; dot = Dot(toLight, norm); } // Clamp dot to zero. if (dot < 0.0f) dot = 0.0f; if (poweredDiffuse) dot = powf(dot, specCoef_); // Attenuation switch (type) { case GE_LIGHTTYPE_DIRECTIONAL: lightScale = 1.0f; break; case GE_LIGHTTYPE_POINT: lightScale = clamp(1.0f / (gstate_c.lightatt[l][0] + gstate_c.lightatt[l][1]*distanceToLight + gstate_c.lightatt[l][2]*distanceToLight*distanceToLight), 0.0f, 1.0f); break; case GE_LIGHTTYPE_SPOT: case GE_LIGHTTYPE_UNKNOWN: lightDir = gstate_c.lightdir[l]; angle = Dot(toLight.Normalized(), lightDir.Normalized()); if (angle >= gstate_c.lightangle[l]) lightScale = clamp(1.0f / (gstate_c.lightatt[l][0] + gstate_c.lightatt[l][1]*distanceToLight + gstate_c.lightatt[l][2]*distanceToLight*distanceToLight), 0.0f, 1.0f) * powf(angle, gstate_c.lightspotCoef[l]); break; default: // ILLEGAL break; } Color4 lightDiff(gstate_c.lightColor[1][l], 0.0f); Color4 diff = (lightDiff * *diffuse) * dot; // Real PSP specular Vec3f toViewer(0,0,1); // Better specular // Vec3f toViewer = (viewer - pos).Normalized(); if (doSpecular) { Vec3f halfVec = (toLight + toViewer); halfVec.Normalize(); dot = Dot(halfVec, norm); if (dot > 0.0f) { Color4 lightSpec(gstate_c.lightColor[2][l], 0.0f); lightSum1 += (lightSpec * *specular * (powf(dot, specCoef_) * lightScale)); } } if (gstate.isLightChanEnabled(l)) { Color4 lightAmbient(gstate_c.lightColor[0][l], 0.0f); lightSum0 += (lightAmbient * *ambient + diff) * lightScale; } } // 4? for (int i = 0; i < 4; i++) { colorOut0[i] = lightSum0[i] > 1.0f ? 1.0f : lightSum0[i]; colorOut1[i] = lightSum1[i] > 1.0f ? 1.0f : lightSum1[i]; } }
vec3 color(const ray& r, hitable *world, int depth) { depth = 0; vec3 clearclr(0.0f, 0.0f, 0.0f); vec3 coeff(1.0f, 1.0f, 1.0f); ray _ray = r; vec3 ret(0.0f, 0.0f, 0.0f); float pdfb = 0.0f; xz_rect ligth_shape(213, 343, 227, 332, 554, nullptr); #ifdef ENABLE_RUSSIAN_ROULETTE for (;;) { #else while (depth < g_maxdepth) { #endif hit_record rec; if (world->hit(_ray, 0.001f, 10000, rec)) { if (rec.mat_ptr->isEmssive()) { // Random direction hits light. float w = 1.0f; if (depth > 0) { // Compute PDF of area light. hitable_pdf p0(&ligth_shape, _ray.org); float pdfl = p0.value(_ray.dir); // Compute weight by using PDF of this ray which the previous hitted object fired. // ライトにヒットしたレイを飛ばしてきたオブジェクトのマテリアル特性によるレイの拡散に関するPDFを使ってウエイトを計算する. //w = pdfb / (pdfb + pdfl); w = ComputeHeuristic(pdfb, pdfl); } auto emit = rec.mat_ptr->emit(rec.u, rec.v, rec.p); ret += coeff * emit * w; return ret; } else { vec3 rr(1.0f, 1.0f, 1.0f); #ifdef ENABLE_RUSSIAN_ROULETTE if (depth > g_rrdepth) { #if 1 auto c = unit_vector(coeff); auto p = math::CMath::Max(c.r(), math::CMath::Max(c.g(), c.b())); auto r = drand48(); if (r > p) { #else auto r = drand48(); if (r > 0.5f) { #endif r = 1.0f / r; rr = vec3(r, r, r); } else { break; } } #endif // Just only getting albedo color. ray scattered; vec3 albedo; float pdf; rec.mat_ptr->scatter(_ray, rec, albedo, scattered, pdf); { // Only for lambert material. cosine_pdf p1(rec.normal); // Generate next ray. auto direction = p1.generate(); scattered = ray(rec.p, direction); // Compute PDF of next ray by this object's material. pdfb = p1.value(scattered.dir); // Compute BRDF of this object's material. auto brdf = rec.mat_ptr->scattering_pdf(r, rec, scattered); coeff *= rr * albedo * brdf / pdfb; } { hitable_pdf p0(&ligth_shape, rec.p); // Generate direction to light. auto lightdir = p0.generate(); ray toLight(rec.p, lightdir); hit_record rec_tolight; if (world->hit(toLight, 0.001f, 10000, rec_tolight)) { if (rec_tolight.mat_ptr->isEmssive()) { // Compute PDF of area light. float pdfl = p0.value(toLight.dir); //float w = pdfl / (pdfb + pdfl); float w = w = ComputeHeuristic(pdfl, pdfb); auto emit = rec_tolight.mat_ptr->emit(rec_tolight.u, rec_tolight.v, rec_tolight.p); ret += coeff * emit / pdfl * w; } } } // Set next ray. _ray = scattered; } } else { return clearclr; } depth++; } return ret; } static void errorCallback(int error, const char* description) { PRINTF("Error: %s\n", description); }