gpt4 book ai didi

javascript - 如何在 Three.js 中正确实现 Cook-Torrance 着色?

转载 作者:行者123 更新时间:2023-12-03 12:27:53 27 4
gpt4 key购买 nike

我正在尝试在 Three.js 中实现 Cook-Torrance 着色算法,我有一个大部分有效的解决方案,但是它没有显示环境光的影响。立方体未被光照亮的侧面是完全黑色的。如果我删除“贝克曼项”,那么我确实可以看到环境光效果:

Image of the cube with completely back faces when not illuminated

同时,用始终返回 0.0 的函数替换 Beckmann,我得到:

Image of the cube with ambient term added to non-illuminated faces.

<小时/>

错误行为的原因似乎是以下方面的划分:

vec3 Specular = (Beckmann(NdotH) * G(NdotH, NdotV, VdotH, NdotL) * R_F(VdotH)) / ( NdotL* NdotV);

如果我将 NdotL * NdotV 修改为 NdotV 并将 gl_FragColor 的计算更改为:

gl_FragColor = vec4(beta * NdotL * (1.0-s)*Kd + beta * s*Specular + ambient*Kd, 1.0);

一切似乎都正常工作。

我不明白的是:为什么?任何地方都没有提到该除法的问题,而且我不能 100% 确定即使剩下的除法也不会在其他情况下引起问题。

<小时/>

这是完整的 MWE:

<html>
<head>
<title>Cook-Torrance BRDF computed by shader</title>
<style>

body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}

canvas {
width: 100%;
height: 100%;
}

</style>
<script src="lib/three.min.js"></script>
<script src="lib/OrbitControls.js"></script>
</head>
<body>

<script type="text/x-glsl" id="vertex">
varying vec3 transformedNormal;
varying vec3 pointPosition;
varying vec3 lightVector;
uniform vec3 pointLightPosition;

void main()
{
transformedNormal = normalMatrix * normal;
pointPosition = (modelViewMatrix * vec4( position, 1.0 )).xyz;
vec4 lPosition = viewMatrix * vec4( pointLightPosition, 1.0 );
lightVector = lPosition.xyz - pointPosition;
gl_Position = projectionMatrix * vec4(pointPosition,1.0);
}
</script>

<script type="text/x-glsl" id="ct-fragment">
uniform vec3 lightPower;
uniform vec3 ambient;
uniform vec3 Kd; // surface diffuse color
uniform vec3 Ks; // surface specular color: equal to R_F(0)
uniform float m; // material roughness (average slope of microfacets)
uniform float s; // percentage of incoming light which is specularly reflected

varying vec3 transformedNormal;
varying vec3 pointPosition;
varying vec3 lightVector;

#define PI 3.14159265

float G(float NdotH, float NdotV, float VdotH, float NdotL)
{
float G1 = 2.0 * NdotH * NdotV / VdotH;
float G2 = 2.0 * NdotH * NdotL / VdotH;
return min( 1.0, min( G1, G2 ));
}

vec3 R_F(float VdotH) {
return Ks + (1.0 - Ks)*pow(1.0-VdotH, 5.0);
}

float Beckmann(float NdotH){
float A = 1.0 / (pow(m,2.0)+pow(NdotH,4.0)*PI);
float B = exp( - pow( tan(acos(NdotH)) , 2.0) / pow(m,2.0));
return A*B;
}

void main()
{
vec3 n = normalize( transformedNormal );
vec3 v = normalize( -pointPosition );
vec3 l = normalize( lightVector );
vec3 h = normalize( v+l );
float NdotH = max(0.0, dot( n, h ));
float VdotH = max(0.0, dot( v, h ));
float NdotV = max(0.0, dot( n, v ));
float NdotL = max(0.0, dot( n, l ));
// specular BRDF
vec3 Specular = (Beckmann(NdotH) * G(NdotH, NdotV, VdotH, NdotL) * R_F(VdotH)) / ( NdotL* NdotV);
vec3 beta = lightPower / ( 4.0 * PI * pow( length(lightVector),2.0) );
gl_FragColor = vec4(beta * NdotL * ((1.0-s)*Kd + s*Specular) + ambient*Kd, 1.0);
}
</script>


<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position = new THREE.Vector3(0,0,5);

var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xf0f0f0 );
document.body.appendChild( renderer.domElement );

controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);

var uniforms = {
Ks: { type: "v3", value: new THREE.Vector3() },
Kd: { type: "v3", value: new THREE.Vector3() },
ambient: { type: "v3", value: new THREE.Vector3() },
pointLightPosition: { type: "v3", value: new THREE.Vector3() },
lightPower: { type: "v3", value: new THREE.Vector3() },
s: {type: "f", value: 0},
m: {type: "f", value: 0}
};

var vs = document.getElementById("vertex").textContent;
var fs = document.getElementById("ct-fragment").textContent;

var material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vs, fragmentShader: fs });

var geometry = new THREE.CubeGeometry(1, 1, 1);
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

light = new THREE.Mesh( new THREE.SphereGeometry( 1, 16, 16), new THREE.MeshBasicMaterial ({color: 0xffff00, wireframe:true}));
light.position = new THREE.Vector3( 10.0, 10.0, 10.0 );
scene.add( light );

uniforms.Ks.value = new THREE.Vector3( 0.95, 0.93, 0.88 );
uniforms.Kd.value = (new THREE.Vector3( 0.50754, 0.50754, 0.50754 ));
uniforms.ambient.value = (new THREE.Vector3( 0.5, 0.5, 0.5 ));
uniforms.pointLightPosition.value = new THREE.Vector3(light.position.x, light.position.y, light.position.z);
uniforms.lightPower.value = new THREE.Vector3( 7000.0, 7000.0, 7000.0 );
uniforms.s.value = 0.5;
uniforms.m.value = 0.1;

function animate() {

requestAnimationFrame( animate );
render();

}

function render() {
controls.update();
renderer.render(scene, camera);
}

animate();
</script>
</body>
</html>

最佳答案

着色方程是 Cook-Torrance 着色模型的数学描述。编写实际的着色器是另一回事,应该考虑到并非所有浮点之间的运算都与方程中的实际数学运算具有相同的属性。

在这种情况下,按 0 潜水会导致问题。事实上,问题在于 Specular 的定义被 0 潜水,但是当分配给 gl_FragColor 时,我再次乘以 NdotL 获得 0 * inf = NaN,并且看起来 NaN 被 GPU 解释为零/负数(从而显示黑色)。

<小时/>

作为引用,正确的 main() 是:

void main()
{
vec3 n = normalize( transformedNormal );
vec3 v = normalize( -pointPosition );
vec3 l = normalize( lightVector );
vec3 h = normalize( v+l );

vec3 specular = vec(0.0, 0.0, 0.0);
float NdotH = max(0.0, dot( n, h ));
float VdotH = max(0.0, dot( v, h ));
float NdotV = max(0.0, dot( n, v ));
float NdotL = max(0.0, dot( n, l ));
if (NdotL > 0 && NdotV > 0)
{
specular = (Beckmann(NdotH) * G(NdotH, NdotV, VdotH, NdotL) * R_F(VdotH)) / ( NdotL* NdotV);
}
vec3 beta = lightPower / ( 4.0 * PI * pow( length(lightVector),2.0) );
gl_FragColor = vec4(beta * NdotL * ((1.0-s)*Kd + s*specular) + ambient*Kd, 1.0);
}

关于javascript - 如何在 Three.js 中正确实现 Cook-Torrance 着色?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24096041/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com