/* Traces a shadow through the object, * pretty much gets the transmission over a ray path */ void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct Isect *last_is) { float hitco[3]; float tr[3] = {1.0, 1.0, 1.0}; Isect is = {{0}}; float *startco, *endco; memset(shr, 0, sizeof(ShadeResult)); /* if 1st hit normal is facing away from the camera, * then we're inside the volume already. */ if (shi->flippednor) { startco = last_is->start; endco = shi->co; } /* trace to find a backface, the other side bounds of the volume */ /* (ray intersect ignores front faces here) */ else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) { startco = shi->co; endco = hitco; } else { shr->combined[0] = shr->combined[1] = shr->combined[2] = 0.f; shr->alpha = shr->combined[3] = 1.f; return; } vol_get_transmittance(shi, tr, startco, endco); /* if we hit another face in the same volume bounds */ /* shift raytrace coordinates to the hit point, to avoid shading volume twice */ /* due to idiosyncracy in ray_trace_shadow_tra() */ if (is.hit.ob == shi->obi) { copy_v3_v3(shi->co, hitco); last_is->dist -= is.dist; shi->vlr = (VlakRen *)is.hit.face; } copy_v3_v3(shr->combined, tr); shr->combined[3] = 1.0f - rgb_to_luma_y(tr); shr->alpha = shr->combined[3]; }
static void vol_shade_one_lamp(struct ShadeInput *shi, const float co[3], const float view[3], LampRen *lar, float lacol[3]) { float visifac, lv[3], lampdist; float tr[3] = {1.0, 1.0, 1.0}; float hitco[3], *atten_co; float p, ref_col[3]; if (lar->mode & LA_LAYER) if ((lar->lay & shi->obi->lay) == 0) return; if ((lar->lay & shi->lay) == 0) return; if (lar->energy == 0.0f) return; if ((visifac = lamp_get_visibility(lar, co, lv, &lampdist)) == 0.f) return; copy_v3_v3(lacol, &lar->r); if (lar->mode & LA_TEXTURE) { shi->osatex = 0; do_lamp_tex(lar, lv, shi, lacol, LA_TEXTURE); } mul_v3_fl(lacol, visifac); if (ELEM(lar->type, LA_SUN, LA_HEMI)) copy_v3_v3(lv, lar->vec); negate_v3(lv); if (shi->mat->vol.shade_type == MA_VOL_SHADE_SHADOWED) { mul_v3_fl(lacol, vol_get_shadow(shi, lar, co)); } else if (ELEM3(shi->mat->vol.shade_type, MA_VOL_SHADE_SHADED, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)) { Isect is; if (shi->mat->vol.shadeflag & MA_VOL_RECV_EXT_SHADOW) { mul_v3_fl(lacol, vol_get_shadow(shi, lar, co)); if (rgb_to_luma_y(lacol) < 0.001f) return; } /* find minimum of volume bounds, or lamp coord */ if (vol_get_bounds(shi, co, lv, hitco, &is, VOL_BOUNDS_SS)) { float dist = len_v3v3(co, hitco); VlakRen *vlr = (VlakRen *)is.hit.face; /* simple internal shadowing */ if (vlr->mat->material_type == MA_TYPE_SURFACE) { lacol[0] = lacol[1] = lacol[2] = 0.0f; return; } if (ELEM(lar->type, LA_SUN, LA_HEMI)) /* infinite lights, can never be inside volume */ atten_co = hitco; else if (lampdist < dist) { atten_co = lar->co; } else atten_co = hitco; vol_get_transmittance(shi, tr, co, atten_co); mul_v3_v3v3(lacol, lacol, tr); } else { /* Point is on the outside edge of the volume, * therefore no attenuation, full transmission. * Radiance from lamp remains unchanged */ } } if (rgb_to_luma_y(lacol) < 0.001f) return; normalize_v3(lv); p = vol_get_phasefunc(shi, shi->mat->vol.asymmetry, view, lv); /* physically based scattering with non-physically based RGB gain */ vol_get_reflection_color(shi, ref_col, co); lacol[0] *= p * ref_col[0]; lacol[1] *= p * ref_col[1]; lacol[2] *= p * ref_col[2]; }
/* For ease of use, I've also introduced a 'reflection' and 'reflection color' parameter, which isn't * physically correct. This works as an RGB tint/gain on out-scattered light, but doesn't affect the light * that is transmitted through the volume. While having wavelength dependent absorption/scattering is more correct, * it also makes it harder to control the overall look of the volume since coloring the outscattered light results * in the inverse color being transmitted through the rest of the volume. */ static void volumeintegrate(struct ShadeInput *shi, float col[4], const float co[3], const float endco[3]) { float radiance[3] = {0.f, 0.f, 0.f}; float tr[3] = {1.f, 1.f, 1.f}; float p[3] = {co[0], co[1], co[2]}; float step_vec[3] = {endco[0] - co[0], endco[1] - co[1], endco[2] - co[2]}; const float stepsize = shi->mat->vol.stepsize; float t0 = 0.f; float pt0 = t0; float t1 = normalize_v3(step_vec); /* returns vector length */ t0 += stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread)); p[0] += t0 * step_vec[0]; p[1] += t0 * step_vec[1]; p[2] += t0 * step_vec[2]; mul_v3_fl(step_vec, stepsize); for (; t0 < t1; pt0 = t0, t0 += stepsize) { const float density = vol_get_density(shi, p); if (density > 0.00001f) { float scatter_col[3] = {0.f, 0.f, 0.f}, emit_col[3]; const float stepd = (t0 - pt0) * density; /* transmittance component (alpha) */ vol_get_transmittance_seg(shi, tr, stepsize, co, density); if (t0 > t1 * 0.25f) { /* only use depth cutoff after we've traced a little way into the volume */ if (rgb_to_luma_y(tr) < shi->mat->vol.depth_cutoff) break; } vol_get_emission(shi, emit_col, p); if (shi->obi->volume_precache) { float p2[3]; p2[0] = p[0] + (step_vec[0] * 0.5f); p2[1] = p[1] + (step_vec[1] * 0.5f); p2[2] = p[2] + (step_vec[2] * 0.5f); vol_get_precached_scattering(&R, shi, scatter_col, p2); } else vol_get_scattering(shi, scatter_col, p, shi->view); radiance[0] += stepd * tr[0] * (emit_col[0] + scatter_col[0]); radiance[1] += stepd * tr[1] * (emit_col[1] + scatter_col[1]); radiance[2] += stepd * tr[2] * (emit_col[2] + scatter_col[2]); } add_v3_v3(p, step_vec); } /* multiply original color (from behind volume) with transmittance over entire distance */ mul_v3_v3v3(col, tr, col); add_v3_v3(col, radiance); /* alpha <-- transmission luminance */ col[3] = 1.0f - rgb_to_luma_y(tr); }
void ColorCorrectionOperation::executePixel(float *output, float x, float y, PixelSampler sampler) { float inputImageColor[4]; float inputMask[4]; this->m_inputImage->read(inputImageColor, x, y, sampler); this->m_inputMask->read(inputMask, x, y, sampler); float level = (inputImageColor[0] + inputImageColor[1] + inputImageColor[2]) / 3.0f; float contrast = this->m_data->master.contrast; float saturation = this->m_data->master.saturation; float gamma = this->m_data->master.gamma; float gain = this->m_data->master.gain; float lift = this->m_data->master.lift; float r, g, b; float value = inputMask[0]; value = min(1.0f, value); const float mvalue = 1.0f - value; float levelShadows = 0.0; float levelMidtones = 0.0; float levelHighlights = 0.0; #define MARGIN 0.10f #define MARGIN_DIV (0.5f / MARGIN) if (level < this->m_data->startmidtones - MARGIN) { levelShadows = 1.0f; } else if (level < this->m_data->startmidtones + MARGIN) { levelMidtones = ((level - this->m_data->startmidtones) * MARGIN_DIV) + 0.5f; levelShadows = 1.0f - levelMidtones; } else if (level < this->m_data->endmidtones - MARGIN) { levelMidtones = 1.0f; } else if (level < this->m_data->endmidtones + MARGIN) { levelHighlights = ((level - this->m_data->endmidtones) * MARGIN_DIV) + 0.5f; levelMidtones = 1.0f - levelHighlights; } else { levelHighlights = 1.0f; } #undef MARGIN #undef MARGIN_DIV contrast *= (levelShadows * this->m_data->shadows.contrast) + (levelMidtones * this->m_data->midtones.contrast) + (levelHighlights * this->m_data->highlights.contrast); saturation *= (levelShadows * this->m_data->shadows.saturation) + (levelMidtones * this->m_data->midtones.saturation) + (levelHighlights * this->m_data->highlights.saturation); gamma *= (levelShadows * this->m_data->shadows.gamma) + (levelMidtones * this->m_data->midtones.gamma) + (levelHighlights * this->m_data->highlights.gamma); gain *= (levelShadows * this->m_data->shadows.gain) + (levelMidtones * this->m_data->midtones.gain) + (levelHighlights * this->m_data->highlights.gain); lift += (levelShadows * this->m_data->shadows.lift) + (levelMidtones * this->m_data->midtones.lift) + (levelHighlights * this->m_data->highlights.lift); float invgamma = 1.0f / gamma; float luma = rgb_to_luma_y(inputImageColor); r = inputImageColor[0]; g = inputImageColor[1]; b = inputImageColor[2]; r = (luma + saturation * (r - luma)); g = (luma + saturation * (g - luma)); b = (luma + saturation * (b - luma)); r = 0.5f + ((r - 0.5f) * contrast); g = 0.5f + ((g - 0.5f) * contrast); b = 0.5f + ((b - 0.5f) * contrast); r = powf(r * gain + lift, invgamma); g = powf(g * gain + lift, invgamma); b = powf(b * gain + lift, invgamma); // mix with mask r = mvalue * inputImageColor[0] + value * r; g = mvalue * inputImageColor[1] + value * g; b = mvalue * inputImageColor[2] + value * b; if (this->m_redChannelEnabled) { output[0] = r; } else { output[0] = inputImageColor[0]; } if (this->m_greenChannelEnabled) { output[1] = g; } else { output[1] = inputImageColor[1]; } if (this->m_blueChannelEnabled) { output[2] = b; } else { output[2] = inputImageColor[2]; } output[3] = inputImageColor[3]; }