// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkGLSLModLight.h" #include "vtkActor.h" #include "vtkCellGridMapper.h" #include "vtkFloatArray.h" #include "vtkInformation.h" #include "vtkInformationObjectBaseKey.h" #include "vtkLightingMapPass.h" #include "vtkLogger.h" #include "vtkObjectFactory.h" #include "vtkOpenGLRenderer.h" #include "vtkPBRFunctions.h" #include "vtkPBRIrradianceTexture.h" #include "vtkPBRLUTTexture.h" #include "vtkPBRPrefilterTexture.h" #include "vtkProperty.h" #include "vtkShaderProgram.h" #include VTK_ABI_NAMESPACE_BEGIN //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkGLSLModLight); //------------------------------------------------------------------------------ vtkGLSLModLight::vtkGLSLModLight() = default; //------------------------------------------------------------------------------ vtkGLSLModLight::~vtkGLSLModLight() = default; //------------------------------------------------------------------------------ void vtkGLSLModLight::PrintSelf(ostream& os, vtkIndent indent) { os << "LastLightComplexity: " << this->LastLightComplexity << "\n"; os << "LastLightCount: " << this->LastLightCount << "\n"; os << "UsePBRTextures: " << this->UsePBRTextures << "\n"; os << "UseAnisotropy: " << this->UseAnisotropy << "\n"; os << "UseClearCoat: " << this->UseClearCoat << "\n"; this->Superclass::PrintSelf(os, indent); } //------------------------------------------------------------------------------ vtkGLSLModLight::LightStatsBasic vtkGLSLModLight::GetBasicLightStats( vtkOpenGLRenderer* renderer, vtkActor* actor) { auto property = actor->GetProperty(); const bool lightsEnabled = property->GetLighting(); // if lighting is enabled, get the lighting information from opengl renderer. LightStatsBasic stats = {}; if (lightsEnabled) { stats.Complexity = renderer->GetLightingComplexity(); stats.Count = renderer->GetLightingCount(); } return stats; } //------------------------------------------------------------------------------ bool vtkGLSLModLight::ReplaceShaderValues(vtkOpenGLRenderer* renderer, std::string& vertexShader, std::string& vtkNotUsed(tessControlShader), std::string& vtkNotUsed(tessEvalShader), std::string& vtkNotUsed(geometryShader), std::string& fragmentShader, vtkAbstractMapper* vtkNotUsed(mapper), vtkActor* actor) { vtkShaderProgram::Substitute( vertexShader, "//VTK::PositionVC::Dec", "smooth out vec4 vertexVCVSOutput;"); vtkShaderProgram::Substitute( fragmentShader, "//VTK::PositionVC::Dec", "smooth in vec4 vertexVCVSOutput;"); vtkShaderProgram::Substitute( fragmentShader, "//VTK::PositionVC::Impl", "vec4 vertexVC = vertexVCVSOutput;"); // Only if normal was not already declared as an output in vertex shader if (!vtkShaderProgram::Substitute( vertexShader, "out vec3 normalVCVSOutput;", "out vec3 normalVCVSOutput;")) { vtkShaderProgram::Substitute( vertexShader, "//VTK::Normal::Dec", "smooth out vec3 normalVCVSOutput;"); } // Only if normal was not already declared as an input in fragment shader if (!vtkShaderProgram::Substitute( fragmentShader, "in vec3 normalVCVSOutput;", "in vec3 normalVCVSOutput;")) { vtkShaderProgram::Substitute( fragmentShader, "//VTK::Normal::Dec", "smooth in vec3 normalVCVSOutput;"); } // Generate code to handle different types of lights. auto info = actor->GetPropertyKeys(); if (info && info->Has(vtkLightingMapPass::RENDER_NORMALS())) { vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", " vec3 n = (normalVCVSOutput + 1.0) * 0.5;\n" " gl_FragData[0] = vec4(n.x, n.y, n.z, 1.0);"); return true; } if (info && info->Has(vtkLightingMapPass::RENDER_LUMINANCE())) { vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", " diffuseColor = vec3(1, 1, 1);\n" " specularColor = vec3(1, 1, 1);\n" " //VTK::Light::Impl\n", false); } auto stats = vtkGLSLModLight::GetBasicLightStats(renderer, actor); this->LastLightComplexity = stats.Complexity; this->LastLightCount = stats.Count; int lastLightComplexity = this->LastLightComplexity; int lastLightCount = this->LastLightCount; if (actor->GetProperty()->GetInterpolation() != VTK_PBR && lastLightCount == 0) { lastLightComplexity = 0; } // Only if vertexNormalVCVS was not already declared in fragment shader if (!vtkShaderProgram::Substitute( fragmentShader, "vec3 vertexNormalVCVS", "vec3 vertexNormalVCVS")) { vtkShaderProgram::Substitute(fragmentShader, "//VTK::Normal::Impl", "vec3 vertexNormalVCVS = normalVCVSOutput;\n" "if (gl_FrontFacing == false) vertexNormalVCVS.z = -vertexNormalVCVS.z;\n" "//VTK::Normal::Impl"); } vtkShaderProgram::Substitute(fragmentShader, "//VTK::Normal::Impl", " vec3 normalizedNormalVCVSOutput = normalize(vertexNormalVCVS);", false); bool hasIBL = false; std::ostringstream oss; if (actor->GetProperty()->GetInterpolation() == VTK_PBR) { // disable default behavior with textures vtkShaderProgram::Substitute(fragmentShader, "//VTK::TCoord::Impl", ""); // get albedo from textures const auto& textures = actor->GetProperty()->GetAllTextures(); bool albedo = false; if (this->UsePBRTextures) { for (auto& t : textures) { if (t.first == "albedoTex") { albedo = true; oss << "vec4 albedoSample = texture(albedoTex, tcoordVCVSOutput);\n" " vec3 albedo = albedoSample.rgb * diffuseColor;\n" " opacity = intensity_opacity * albedoSample.a;\n"; } } } if (!albedo) { // VTK colors are expressed in linear color space oss << "vec3 albedo = diffuseColor;\n"; } vtkOpenGLRenderer* oglRen = vtkOpenGLRenderer::SafeDownCast(renderer); if (lastLightComplexity > 0) { // PBR functions vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Dec", vtkPBRFunctions); // get material/emissive from textures bool material = false; bool emissive = false; if (this->UsePBRTextures) { for (auto& t : textures) { if (t.first == "materialTex") { // we are using GLTF specification here with a combined texture holding values for AO, // roughness and metallic on R,G,B channels respectively material = true; oss << " vec4 material = texture(materialTex, tcoordVCVSOutput);\n" " float roughness = material.g * roughnessUniform;\n" " float metallic = material.b * metallicUniform;\n" " float ao = material.r;\n"; } else if (t.first == "emissiveTex") { emissive = true; oss << " vec3 emissiveColor = texture(emissiveTex, tcoordVCVSOutput).rgb;\n" " emissiveColor = emissiveColor * emissiveFactorUniform;\n"; } // Anisotropy texture is sampled by mappers. } } if (!material) { oss << " float roughness = roughnessUniform;\n"; oss << " float metallic = metallicUniform;\n"; oss << " float ao = 1.0;\n"; } if (!emissive) { oss << " vec3 emissiveColor = vec3(0.0);\n"; } // IBL if (oglRen && renderer->GetUseImageBasedLighting()) { hasIBL = true; oss << " const float prefilterMaxLevel = float(" << (oglRen->GetEnvMapPrefiltered()->GetPrefilterLevels() - 1) << ");\n"; } oss << " vec3 N = normalizedNormalVCVSOutput;\n" " vec3 V = normalize(-vertexVC.xyz);\n" " float NdV = clamp(dot(N, V), 1e-5, 1.0);\n"; if (this->UseAnisotropy) { // Load anisotropic functions vtkShaderProgram::Substitute(fragmentShader, "//VTK::Define::Dec", "#define ANISOTROPY\n" "//VTK::Define::Dec"); // Precompute anisotropic parameters // at and ab are the roughness along the tangent and bitangent // Disney, as in OSPray oss << " float r2 = roughness * roughness;\n" " float aspect = sqrt(1.0 - 0.9 * anisotropy);\n"; oss << " float at = max(r2 / aspect, 0.001);\n" " float ab = max(r2 * aspect, 0.001);\n"; oss << " float TdV = dot(tangentVC, V);\n" " float BdV = dot(bitangentVC, V);\n"; } if (this->UseClearCoat) { // Load clear coat uniforms vtkShaderProgram::Substitute(fragmentShader, "//VTK::Define::Dec", "#define CLEAR_COAT\n" "//VTK::Define::Dec"); // Clear coat parameters oss << " vec3 coatN = coatNormalVCVSOutput;\n"; oss << " float coatRoughness = coatRoughnessUniform;\n"; oss << " float coatStrength = coatStrengthUniform;\n"; oss << " float coatNdV = clamp(dot(coatN, V), 1e-5, 1.0);\n"; } if (hasIBL) { if (!oglRen->GetUseSphericalHarmonics()) { oss << " vec3 irradiance = texture(irradianceTex, envMatrix*N).rgb;\n"; } else { oss << " vec3 rotN = envMatrix * N;\n"; oss << " vec3 irradiance = vec3(ComputeSH(rotN, shRed), ComputeSH(rotN, shGreen), " "ComputeSH(rotN, shBlue));\n"; } if (this->UseAnisotropy) { oss << " vec3 anisotropicTangent = cross(bitangentVC, V);\n" " vec3 anisotropicNormal = cross(anisotropicTangent, bitangentVC);\n" " vec3 bentNormal = normalize(mix(N, anisotropicNormal, anisotropy));\n" " vec3 worldReflect = normalize(envMatrix*reflect(-V, bentNormal));\n"; } else { oss << " vec3 worldReflect = normalize(envMatrix*reflect(-V, N));\n"; } oss << " vec3 prefilteredSpecularColor = textureLod(prefilterTex, worldReflect," " roughness * prefilterMaxLevel).rgb;\n"; oss << " vec2 brdf = texture(brdfTex, vec2(NdV, roughness)).rg;\n"; // Use the same prefilter texture for clear coat but with the clear coat roughness and // normal if (this->UseClearCoat) { oss << " vec3 coatWorldReflect = normalize(envMatrix*reflect(-V,coatN));\n" " vec3 prefilteredSpecularCoatColor = textureLod(prefilterTex, coatWorldReflect," " coatRoughness * prefilterMaxLevel).rgb;\n" " vec2 coatBrdf = texture(brdfTex, vec2(coatNdV, coatRoughness)).rg;\n"; } } else { oss << " vec3 irradiance = vec3(0.0);\n"; oss << " vec3 prefilteredSpecularColor = vec3(0.0);\n"; oss << " vec2 brdf = vec2(0.0, 0.0);\n"; if (this->UseClearCoat) { oss << " vec3 prefilteredSpecularCoatColor = vec3(0.0);\n"; oss << " vec2 coatBrdf = vec2(0.0);\n"; } } oss << " vec3 Lo = vec3(0.0);\n"; oss << " vec3 F0 = mix(vec3(baseF0Uniform), albedo, metallic);\n" // specular occlusion, it affects only material with an f0 < 0.02, // else f90 is 1.0 " float f90 = clamp(dot(F0, vec3(50.0 * 0.33)), 0.0, 1.0);\n" " vec3 F90 = mix(vec3(f90), edgeTintUniform, metallic);\n" " vec3 L, H, radiance, F, specular, diffuse;\n" " float NdL, NdH, HdL, distanceVC, attenuation, D, Vis;\n\n"; if (this->UseClearCoat) { // Coat layer is dielectric so F0 and F90 are achromatic oss << " vec3 coatF0 = vec3(coatF0Uniform);\n" " vec3 coatF90 = vec3(1.0);\n" " vec3 coatLayer, Fc;\n" " float coatNdL, coatNdH;\n" " vec3 coatColorFactor = mix(vec3(1.0), coatColorUniform, coatStrength);\n"; } } oss << "//VTK::Light::Impl\n"; vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", oss.str(), false); oss.clear(); oss.str(""); if (hasIBL) { oss << "//VTK::Light::Dec\n" "uniform mat3 envMatrix;\n" "uniform sampler2D brdfTex;\n" "uniform samplerCube prefilterTex;\n"; if (oglRen->GetUseSphericalHarmonics()) { oss << "uniform float shRed[9];\n" "uniform float shGreen[9];\n" "uniform float shBlue[9];\n" "float ComputeSH(vec3 n, float sh[9])\n" "{\n" " float v = 0.0;\n" " v += sh[0];\n" " v += sh[1] * n.y;\n" " v += sh[2] * n.z;\n" " v += sh[3] * n.x;\n" " v += sh[4] * n.x * n.y;\n" " v += sh[5] * n.y * n.z;\n" " v += sh[6] * (3.0 * n.z * n.z - 1.0);\n" " v += sh[7] * n.x * n.z;\n" " v += sh[8] * (n.x * n.x - n.y * n.y);\n" " return max(v, 0.0);\n" "}\n"; } else { oss << "uniform samplerCube irradianceTex;\n"; } // add uniforms vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Dec", oss.str()); oss.clear(); oss.str(""); } } // get standard lighting declarations. vtkShaderProgram::Substitute( fragmentShader, "//VTK::Light::Dec", renderer->GetLightingUniforms()); oss.str(""); switch (lastLightComplexity) { case 0: // no lighting if (actor->GetProperty()->GetInterpolation() == VTK_PBR) { vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", "gl_FragData[0] = vec4(albedo, opacity);\n" " //VTK::Light::Impl\n", false); } else { vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", "gl_FragData[0] = vec4(ambientColor + diffuseColor, opacity);\n" " //VTK::Light::Impl\n", false); } break; case 1: // headlight if (actor->GetProperty()->GetInterpolation() == VTK_PBR) { // L = V = H for headlights if (this->UseAnisotropy) { // When V=H, maybe can be optimised oss << "specular = SpecularAnisotropic(at, ab, V, tangentVC, bitangentVC, V, TdV, " "BdV, NdV, NdV, NdV,\n" "1.0, roughness, anisotropy, F0, F90, F);\n"; } else { oss << "specular = SpecularIsotropic(NdV, NdV, NdV, 1.0, roughness, F0, F90, F);\n"; } oss << " diffuse = (1.0 - metallic) * (1.0 - F) * DiffuseLambert(albedo);\n" " radiance = lightColor0;\n"; if (this->UseClearCoat) { oss << " // Clear coat is isotropic\n" " coatLayer = SpecularIsotropic(coatNdV, coatNdV, coatNdV, 1.0," " coatRoughness, coatF0, coatF90, Fc) * radiance * coatNdV * coatStrength;\n" " Fc *= coatStrength;\n" " radiance *= coatColorFactor;\n" " specular *= (1.0 - Fc) * (1.0 - Fc);\n" " diffuse *= (1.0 - Fc);\n" " Lo += coatLayer;\n"; } oss << " Lo += radiance * (diffuse + specular) * NdV;\n\n" "//VTK::Light::Impl\n"; } else { oss << "float df = max(0.0f, normalizedNormalVCVSOutput.z);\n" " float sf = pow(df, power_specular);\n" " vec3 diffuse = df * diffuseColor * lightColor0;\n" " vec3 specular = sf * specularColor * lightColor0;\n" " gl_FragData[0] = vec4(ambientColor + diffuse + specular, opacity);\n" " //VTK::Light::Impl\n"; } vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", oss.str(), false); break; case 2: // light kit if (actor->GetProperty()->GetInterpolation() == VTK_PBR) { for (int i = 0; i < lastLightCount; ++i) { oss << " L = normalize(-lightDirectionVC" << i << ");\n" " H = normalize(V + L);\n" " HdL = clamp(dot(H, L), 1e-5, 1.0);\n" " NdL = clamp(dot(N, L), 1e-5, 1.0);\n" " NdH = clamp(dot(N, H), 1e-5, 1.0);\n" " radiance = lightColor" << i << ";\n"; if (this->UseAnisotropy) { oss << " specular = SpecularAnisotropic(at, ab, L, tangentVC, bitangentVC, H, " "TdV, BdV, NdH, NdV, NdL, HdL, roughness, anisotropy, F0, F90, F);\n"; } else { oss << " specular = SpecularIsotropic(NdH, NdV, NdL, HdL, roughness, F0, F90, F);\n"; } oss << " diffuse = (1.0 - metallic) * (1.0 - F) * DiffuseLambert(albedo);\n"; if (this->UseClearCoat) { oss << " coatNdL = clamp(dot(coatN, L), 1e-5, 1.0);\n" " coatNdH = clamp(dot(coatN, H), 1e-5, 1.0);\n" " // Clear coat is isotropic\n" " coatLayer = SpecularIsotropic(coatNdH, coatNdV, coatNdL, HdL," " coatRoughness, coatF0, coatF90, Fc) * radiance * coatNdL * coatStrength;\n" " // Energy compensation depending on how much light is reflected by the " "coat layer\n" " Fc *= coatStrength;\n" " specular *= (1.0 - Fc) * (1.0 - Fc);\n" " diffuse *= (1.0 - Fc);\n" " radiance *= coatColorFactor;\n" " Lo += coatLayer;\n"; } oss << " Lo += radiance * (diffuse + specular) * NdL;\n"; } oss << "//VTK::Light::Impl\n"; } else { oss << " vec3 diffuse = vec3(0,0,0);\n" " vec3 specular = vec3(0,0,0);\n" " float df;\n" " float sf;\n"; for (int i = 0; i < lastLightCount; ++i) { oss << " df = max(0.0, dot(normalizedNormalVCVSOutput, -lightDirectionVC" << i << "));\n" // if you change the next line also change vtkShadowMapPass " diffuse += (df * lightColor" << i << ");\n" << " sf = sign(df)*pow(max(1e-5, dot( reflect(lightDirectionVC" << i << ", normalizedNormalVCVSOutput), normalize(-vertexVC.xyz))), " "power_specular);\n" // if you change the next line also change vtkShadowMapPass " specular += (sf * lightColor" << i << ");\n"; } oss << " diffuse = diffuse * diffuseColor;\n" " specular = specular * specularColor;\n" " gl_FragData[0] = vec4(ambientColor + diffuse + specular, opacity);" " //VTK::Light::Impl"; } vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", oss.str(), false); break; case 3: // positional if (actor->GetProperty()->GetInterpolation() == VTK_PBR) { for (int i = 0; i < lastLightCount; ++i) { oss << " if (lightPositional" << i << " == 0) {\n" " attenuation = 1.0;\n" " L = -lightDirectionVC" << i << ";\n" " } else {\n" " L = lightPositionVC" << i << " - vertexVC.xyz;\n" " distanceVC = length(L);\n" " L = normalize(L);\n" " attenuation = 1.0 / (lightAttenuation" << i << ".x\n" " + lightAttenuation" << i << ".y * distanceVC\n" " + lightAttenuation" << i << ".z * distanceVC * distanceVC);\n" " // cone angle is less than 90 for a spot light\n" " if (lightConeAngle" << i << " < 90.0) {\n" " float coneDot = dot(-L, lightDirectionVC" << i << ");\n" " // if inside the cone\n" " if (coneDot >= cos(radians(lightConeAngle" << i << ")))\n" " {\n" " attenuation = attenuation * pow(coneDot, lightExponent" << i << ");\n" " }\n" " else\n" " {\n" " attenuation = 0.0;\n" " }\n" " }\n" " }\n" " H = normalize(V + L);\n" " NdL = clamp(dot(N, L), 1e-5, 1.0);\n" " NdH = clamp(dot(N, H), 1e-5, 1.0);\n" " HdL = clamp(dot(H, L), 1e-5, 1.0);\n" " radiance = lightColor" << i << ";\n" " radiance *= attenuation;\n"; if (this->UseAnisotropy) { oss << " specular = SpecularAnisotropic(at, ab, L, tangentVC, bitangentVC, H, " "TdV, BdV, NdH, NdV, NdL, HdL, roughness, anisotropy, F0, F90, F);\n"; } else { oss << " specular = SpecularIsotropic(NdH, NdV, NdL, HdL, roughness, F0, F90, F);\n"; } oss << " diffuse = (1.0 - metallic) * (1.0 - F) * DiffuseLambert(albedo);\n"; if (this->UseClearCoat) { oss << " coatNdL = clamp(dot(coatN, L), 1e-5, 1.0);\n" " coatNdH = clamp(dot(coatN, H), 1e-5, 1.0);\n" " // Clear coat is isotropic\n" " coatLayer = SpecularIsotropic(coatNdH, coatNdV, coatNdL, HdL," " coatRoughness, coatF0, coatF90, Fc) * radiance * coatNdL * coatStrength;\n" " // Energy compensation depending on how much light is reflected by the " "coat layer\n" " Fc *= coatStrength;\n" " specular *= (1.0 - Fc) * (1.0 - Fc);\n" " diffuse *= (1.0 - Fc);\n" " radiance *= coatColorFactor;\n" " Lo += coatLayer;\n"; } oss << " Lo += radiance * (diffuse + specular) * NdL;\n"; } oss << "//VTK::Light::Impl\n"; } else { oss << " vec3 diffuse = vec3(0,0,0);\n" " vec3 specular = vec3(0,0,0);\n" " vec3 vertLightDirectionVC;\n" " float attenuation;\n" " float df;\n" " float sf;\n"; for (int i = 0; i < lastLightCount; ++i) { oss << " attenuation = 1.0;\n" " if (lightPositional" << i << " == 0) {\n" " vertLightDirectionVC = lightDirectionVC" << i << "; }\n" " else {\n" " vertLightDirectionVC = vertexVC.xyz - lightPositionVC" << i << ";\n" " float distanceVC = length(vertLightDirectionVC);\n" " vertLightDirectionVC = normalize(vertLightDirectionVC);\n" " attenuation = 1.0 /\n" " (lightAttenuation" << i << ".x\n" " + lightAttenuation" << i << ".y * distanceVC\n" " + lightAttenuation" << i << ".z * distanceVC * distanceVC);\n" " // cone angle is less than 90 for a spot light\n" " if (lightConeAngle" << i << " < 90.0) {\n" " float coneDot = dot(vertLightDirectionVC, lightDirectionVC" << i << ");\n" " // if inside the cone\n" " if (coneDot >= cos(radians(lightConeAngle" << i << "))) {\n" " attenuation = attenuation * pow(coneDot, lightExponent" << i << "); }\n" " else {\n" " attenuation = 0.0; }\n" " }\n" " }\n" << " df = max(0.0,attenuation*dot(normalizedNormalVCVSOutput, " "-vertLightDirectionVC));\n" // if you change the next line also change vtkShadowMapPass " diffuse += (df * lightColor" << i << ");\n" " sf = sign(df)*attenuation*pow( max(1e-5, dot( reflect(vertLightDirectionVC, " "normalizedNormalVCVSOutput), normalize(-vertexVC.xyz))), power_specular);\n" // if you change the next line also change vtkShadowMapPass " specular += (sf * lightColor" << i << ");\n"; } oss << " diffuse = diffuse * diffuseColor;\n" " specular = specular * specularColor;\n" " gl_FragData[0] = vec4(ambientColor + diffuse + specular, opacity);" " //VTK::Light::Impl"; } vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", oss.str(), false); break; default: break; } if (actor->GetProperty()->GetInterpolation() == VTK_PBR) { oss.str(""); if (lastLightComplexity > 0) { if (hasIBL) { oss << " // Multi-scatter approximation: see https://bruop.github.io/ibl/\n" " diffuse = (1.0 - metallic) * (1.0 - 0.04) * albedo;\n" " vec3 Fr = max(vec3(1 - roughness), F0) - F0;\n" " vec3 k_S = F0 + Fr * pow(1.0 - NdV, 5.0);\n" " vec3 FssEss = k_S * brdf.r + F90 * brdf.g;\n" " float Ems = 1.0 - (brdf.r + brdf.g);\n" " vec3 F_avg = F0 + (1.0 - F0) / 21.0;\n" " vec3 FmsEms = Ems * FssEss * F_avg / max(1.0 - F_avg * Ems, vec3(1e-5));\n" " vec3 k_D = diffuse * (1.0 - FssEss - FmsEms);\n" " vec3 iblSpecular = FssEss * prefilteredSpecularColor;\n" " vec3 iblDiffuse = (FmsEms + k_D) * irradiance;\n"; } else { oss << " vec3 iblSpecular = vec3(0.0);\n" " vec3 iblDiffuse = vec3(0.0);\n"; } oss << " vec3 color = iblDiffuse + iblSpecular;\n" "\n"; if (this->UseClearCoat) { oss << " // Clear coat attenuation\n" " Fc = F_Schlick(coatF0, coatF90, coatNdV) * coatStrength;\n" " iblSpecular *= (1.0 - Fc);\n" " iblDiffuse *= (1.0 - Fc) * (1.0 - Fc);\n" " // Clear coat specular\n" " vec3 iblSpecularClearCoat = prefilteredSpecularCoatColor * (coatF0 * coatBrdf.r + " "coatBrdf.g) * Fc;\n" // Color absorption by the coat layer " color *= coatColorFactor;\n" " color += iblSpecularClearCoat;\n" "\n"; } oss << " color += Lo;\n" " color = mix(color, color * ao, aoStrengthUniform);\n" // ambient occlusion " color += emissiveColor;\n" // emissive " gl_FragData[0] = vec4(color, opacity);\n"; } oss << " gl_FragData[0].rgb = pow(gl_FragData[0].rgb, vec3(1.0/2.2));\n" " //VTK::Light::Impl"; vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", oss.str(), false); } // If rendering luminance values, write those values to the fragment if (info && info->Has(vtkLightingMapPass::RENDER_LUMINANCE())) { switch (this->LastLightComplexity) { case 0: // no lighting vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", " gl_FragData[0] = vec4(0.0, 0.0, 0.0, 1.0);\n" " //VTK::Light::Impl", false); break; case 1: // headlight case 2: // light kit case 3: // positional vtkShaderProgram::Substitute(fragmentShader, "//VTK::Light::Impl", " float ambientY = dot(vec3(0.2126, 0.7152, 0.0722), ambientColor);\n" " gl_FragData[0] = vec4(ambientY, diffuse.x, specular.x, 1.0);\n" " //VTK::Light::Impl", false); break; } } return true; } //------------------------------------------------------------------------------ bool vtkGLSLModLight::SetShaderParameters(vtkOpenGLRenderer* renderer, vtkShaderProgram* program, vtkAbstractMapper* vtkNotUsed(mapper), vtkActor* actor, vtkOpenGLVertexArrayObject* vtkNotUsed(VAO) /*=nullptr*/) { vtkOpenGLRenderer* oglRen = vtkOpenGLRenderer::SafeDownCast(renderer); if (oglRen) { vtkFloatArray* sh = oglRen->GetSphericalHarmonics(); if (oglRen->GetUseSphericalHarmonics() && sh) { std::string uniforms[3] = { "shRed", "shGreen", "shBlue" }; for (int i = 0; i < 3; i++) { float coeffs[9]; sh->GetTypedTuple(i, coeffs); // predivide with pi for Lambertian diffuse coeffs[0] *= 0.282095f; coeffs[1] *= -0.488603f * (2.f / 3.f); coeffs[2] *= 0.488603f * (2.f / 3.f); coeffs[3] *= -0.488603f * (2.f / 3.f); coeffs[4] *= 1.092548f * 0.25f; coeffs[5] *= -1.092548f * 0.25f; coeffs[6] *= 0.315392f * 0.25f; coeffs[7] *= -1.092548f * 0.25f; coeffs[8] *= 0.546274f * 0.25f; program->SetUniform1fv(uniforms[i].c_str(), 9, coeffs); } } } oglRen->UpdateLightingUniforms(program); // Add IBL textures if (oglRen->GetUseImageBasedLighting()) { program->SetUniformi("brdfTex", oglRen->GetEnvMapLookupTable()->GetTextureUnit()); program->SetUniformi("prefilterTex", oglRen->GetEnvMapPrefiltered()->GetTextureUnit()); if (!oglRen->GetUseSphericalHarmonics()) { program->SetUniformi("irradianceTex", oglRen->GetEnvMapIrradiance()->GetTextureUnit()); } } // apply vtkProperty attributes // FIXME: Follow a consistent naming convention for shader uniforms. vtkProperty* ppty = actor->GetProperty(); program->SetUniformf("intensity_opacity", ppty->GetOpacity()); program->SetUniformf("intensity_ambient", ppty->GetAmbient()); program->SetUniformf("intensity_diffuse", ppty->GetDiffuse()); program->SetUniformf("intensity_specular", ppty->GetSpecular()); program->SetUniform3f("color_ambient", ppty->GetAmbientColor()); program->SetUniform3f("color_diffuse", ppty->GetDiffuseColor()); program->SetUniform3f("color_specular", ppty->GetSpecularColor()); program->SetUniformi("enable_specular", ppty->GetLighting()); program->SetUniformf("power_specular", ppty->GetSpecularPower()); if (auto bfPpty = actor->GetBackfaceProperty()) { program->SetUniformf("intensity_opacity_bf", bfPpty->GetOpacity()); program->SetUniformf("intensity_ambient_bf", bfPpty->GetAmbient()); program->SetUniformf("intensity_diffuse_bf", bfPpty->GetDiffuse()); program->SetUniformf("intensity_specular_bf", bfPpty->GetSpecular()); program->SetUniform3f("color_ambient_bf", bfPpty->GetAmbientColor()); program->SetUniform3f("color_diffuse_bf", bfPpty->GetDiffuseColor()); program->SetUniform3f("color_specular_bf", bfPpty->GetSpecularColor()); program->SetUniformi("enable_specular_bf", bfPpty->GetLighting()); program->SetUniformf("power_specular_bf", bfPpty->GetSpecularPower()); } program->SetUniformf("normalScaleUniform", static_cast(ppty->GetNormalScale())); if (actor->GetProperty()->GetInterpolation() == VTK_PBR && this->LastLightComplexity > 0) { program->SetUniformf("metallicUniform", static_cast(ppty->GetMetallic())); program->SetUniformf("roughnessUniform", static_cast(ppty->GetRoughness())); program->SetUniformf("aoStrengthUniform", static_cast(ppty->GetOcclusionStrength())); program->SetUniform3f("emissiveFactorUniform", ppty->GetEmissiveFactor()); program->SetUniform3f("edgeTintUniform", ppty->GetEdgeTint()); if (ppty->GetAnisotropy() > 0.0) { program->SetUniformf("anisotropyUniform", static_cast(ppty->GetAnisotropy())); program->SetUniformf( "anisotropyRotationUniform", static_cast(ppty->GetAnisotropyRotation())); } if (ppty->GetCoatStrength() > 0.0) { // Compute the reflectance of the coat layer and the exterior // Hard coded air environment (ior = 1.0) constexpr double environmentIOR = 1.0; program->SetUniformf("coatF0Uniform", static_cast( vtkProperty::ComputeReflectanceFromIOR(ppty->GetCoatIOR(), environmentIOR))); program->SetUniform3f("coatColorUniform", ppty->GetCoatColor()); program->SetUniformf("coatStrengthUniform", static_cast(ppty->GetCoatStrength())); program->SetUniformf("coatRoughnessUniform", static_cast(ppty->GetCoatRoughness())); program->SetUniformf( "coatNormalScaleUniform", static_cast(ppty->GetCoatNormalScale())); } // Compute the reflectance of the base layer program->SetUniformf( "baseF0Uniform", static_cast(ppty->ComputeReflectanceOfBaseLayer())); } return true; } //------------------------------------------------------------------------------ bool vtkGLSLModLight::IsUpToDate( vtkOpenGLRenderer* renderer, vtkAbstractMapper* vtkNotUsed(mapper), vtkActor* actor) { auto stats = vtkGLSLModLight::GetBasicLightStats(renderer, actor); if (this->LastLightComplexity != stats.Complexity || this->LastLightCount != stats.Count) { // lighting is not up to date. return false; } // all good return true; } VTK_ABI_NAMESPACE_END