GLSL 中阴影光线的平滑阴影无法正常工作
Smooth shadows from shadow rays in GLSL not working correctly
我正在 WebGL 中实现带有网格点的光线追踪器。我的实施几句话。
我在纹理中传递顶点值和顶点法线,并在执行光线追踪算法的片段着色器中解压缩它们。由三角形组成的球体具有平坦的阴影外观,然后我将三角形的顶点法线与重心坐标相乘,我在三角形球体中获得了平滑的外观。问题是当我计算阴影时,它具有相同的 "edgy" 外观。我将提供结果的打印屏幕以及我当前如何计算阴影光线的代码。
如您所见,球体表面看起来很光滑,但阴影却不是。
这是我使用的 calcShadow 函数:
vec3 calcShadow(Sphere lightSource, vec3 hitPos){
vec3 color;
vec3 lightDir = normalize(lightSource.center-hitPos);
Ray shadowRay = Ray(hitPos, lightDir);
float isHitLight = hitLightSource(shadowRay,lightSource);
float isHitMesh = hitMesh(shadowRay);
if (isHitMesh < isHitLight) {
color = vec3(0.5,0.5,0.5);
}else{
color = vec3(1.,1.,1.);
}
return color;
}
hitMesh:
float hitMesh(Ray R_){
float mindist = 1000.;
vec4 a = vec4(0.0), b = vec4(0.0), c = vec4(0.0);
vec3 intersect = vec3(0.0,0.0,0.0);
for (int i = 0; i < vertsCount; i += 3) {
a = texelFetch(uMeshData, ivec2(i, 0), 0);
b = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(1, 0));
c = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(2, 0));
vec3 triangleNormal;
vec3 uvt;
float z;
bool isHit = hitTriangleSecond(R_.orig, R_.dir, a.xyz, b.xyz, c.xyz, uvt, triangleNormal, intersect, z);;
if (isHit) {
if (z<mindist && z > 0.001) mindist = z;
}
}
return mindist;
}
这是三角形相交测试的hitTriangke第二种方法:
bool hitTriangleSecond( vec3 orig, vec3 dir, vec3 a, vec3 b, vec3 c,
out vec3 uvt, out vec3 N, out vec3 x, out float dist) {
float eps=1e-8;
vec3 ab = b - a;
vec3 ac = c - a;
N = normalize(cross(ab, ac));
dist = dot(a - orig, N) / dot(dir, N);
x = orig + dir * dist;
vec3 ax = x - a;
float d00 = dot(ab, ab);
float d01 = dot(ab, ac);
float d11 = dot(ac, ac);
float d20 = dot(ax, ab);
float d21 = dot(ax, ac);
float denom = d00 * d11 - d01 * d01; // determinant
// if the determinant is negative the triangle is backfacing
// if the determinant is close to 0, the ray misses the triangl
if ( denom <= eps )
return false;
uvt.y = (d11 * d20 - d01 * d21) / denom;
if ( uvt.y < 0.0 || uvt.y > 1.0 )
return false;
uvt.z = (d00 * d21 - d01 * d20) / denom;
if ( uvt.z < 0.0 || uvt.z > 1.0 )
return false;
uvt.x = 1.0 - uvt.y - uvt.z;
if ( uvt.x < 0.0 || uvt.x > 1.0 )
return false;
return true;
}
我知道没有计算内插三角形法线,但我的问题是,我是否需要根据顶点法线将 hitTriangle 函数更新为 return 内插法线?或者我需要计算重心坐标然后将其插入命中三角函数?我有点不明白如何实现平滑阴影...
下面是我的循环跟踪,我在其中解压缩网格并计算命中的插值法线,这就是我获得光滑表面外观的方式:
for (int i = 0; i < vertsCount; i += 3) {
a = texelFetch(uMeshData, ivec2(i, 0), 0);
b = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(1, 0));
c = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(2, 0));
aN = texelFetch(uNormData, ivec2(i, 0), 0);
bN = texelFetchOffset(uNormData, ivec2(i, 0), 0, ivec2(1, 0));
cN = texelFetchOffset(uNormData, ivec2(i, 0), 0, ivec2(2, 0));
vec3 uvt;
vec3 intersect;
float z;
bool isHit = hitTriangleSecond(R_.orig, R_.dir, a.xyz, b.xyz, c.xyz, uvt, triangleNormal, intersect, z);
if (isHit) {
if (z<mindist && z > 0.001) {
hitPos1 = intersect;
mindist = z;
weHitSomething = true;
material.type = DIEL;
material.albedo = vec3(.8, .3, .4);
normal = aN.xyz*uvt.x + bN.xyz*uvt.y + cN.xyz*uvt.z;
hitPos = hitPos1;
}
}
}
用于光线命中测试的网格仍然是低分辨率三角形网格,即使您为了校正照明计算而乱用法线,所以阴影中的尖角有点在意料之中。
光线投射依赖于三角形相交,因此要获得平滑的阴影,您需要阴影投射器中的平滑几何体 - 即更多三角形。法线贴图等技术适用于照明,因为您可以根据修改后的方向伪造一个近似的光强度,但实际上并没有任何表面位移会改变物理几何形状,因此这无助于纠正任何不正确的命中测试。
你真正需要的阴影是更多的几何形状,这样你就可以使用真实的三角形准确地描述阴影投射轮廓边缘的形状。
我正在 WebGL 中实现带有网格点的光线追踪器。我的实施几句话。
我在纹理中传递顶点值和顶点法线,并在执行光线追踪算法的片段着色器中解压缩它们。由三角形组成的球体具有平坦的阴影外观,然后我将三角形的顶点法线与重心坐标相乘,我在三角形球体中获得了平滑的外观。问题是当我计算阴影时,它具有相同的 "edgy" 外观。我将提供结果的打印屏幕以及我当前如何计算阴影光线的代码。
如您所见,球体表面看起来很光滑,但阴影却不是。
这是我使用的 calcShadow 函数:
vec3 calcShadow(Sphere lightSource, vec3 hitPos){
vec3 color;
vec3 lightDir = normalize(lightSource.center-hitPos);
Ray shadowRay = Ray(hitPos, lightDir);
float isHitLight = hitLightSource(shadowRay,lightSource);
float isHitMesh = hitMesh(shadowRay);
if (isHitMesh < isHitLight) {
color = vec3(0.5,0.5,0.5);
}else{
color = vec3(1.,1.,1.);
}
return color;
}
hitMesh:
float hitMesh(Ray R_){
float mindist = 1000.;
vec4 a = vec4(0.0), b = vec4(0.0), c = vec4(0.0);
vec3 intersect = vec3(0.0,0.0,0.0);
for (int i = 0; i < vertsCount; i += 3) {
a = texelFetch(uMeshData, ivec2(i, 0), 0);
b = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(1, 0));
c = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(2, 0));
vec3 triangleNormal;
vec3 uvt;
float z;
bool isHit = hitTriangleSecond(R_.orig, R_.dir, a.xyz, b.xyz, c.xyz, uvt, triangleNormal, intersect, z);;
if (isHit) {
if (z<mindist && z > 0.001) mindist = z;
}
}
return mindist;
}
这是三角形相交测试的hitTriangke第二种方法:
bool hitTriangleSecond( vec3 orig, vec3 dir, vec3 a, vec3 b, vec3 c,
out vec3 uvt, out vec3 N, out vec3 x, out float dist) {
float eps=1e-8;
vec3 ab = b - a;
vec3 ac = c - a;
N = normalize(cross(ab, ac));
dist = dot(a - orig, N) / dot(dir, N);
x = orig + dir * dist;
vec3 ax = x - a;
float d00 = dot(ab, ab);
float d01 = dot(ab, ac);
float d11 = dot(ac, ac);
float d20 = dot(ax, ab);
float d21 = dot(ax, ac);
float denom = d00 * d11 - d01 * d01; // determinant
// if the determinant is negative the triangle is backfacing
// if the determinant is close to 0, the ray misses the triangl
if ( denom <= eps )
return false;
uvt.y = (d11 * d20 - d01 * d21) / denom;
if ( uvt.y < 0.0 || uvt.y > 1.0 )
return false;
uvt.z = (d00 * d21 - d01 * d20) / denom;
if ( uvt.z < 0.0 || uvt.z > 1.0 )
return false;
uvt.x = 1.0 - uvt.y - uvt.z;
if ( uvt.x < 0.0 || uvt.x > 1.0 )
return false;
return true;
}
我知道没有计算内插三角形法线,但我的问题是,我是否需要根据顶点法线将 hitTriangle 函数更新为 return 内插法线?或者我需要计算重心坐标然后将其插入命中三角函数?我有点不明白如何实现平滑阴影...
下面是我的循环跟踪,我在其中解压缩网格并计算命中的插值法线,这就是我获得光滑表面外观的方式:
for (int i = 0; i < vertsCount; i += 3) {
a = texelFetch(uMeshData, ivec2(i, 0), 0);
b = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(1, 0));
c = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(2, 0));
aN = texelFetch(uNormData, ivec2(i, 0), 0);
bN = texelFetchOffset(uNormData, ivec2(i, 0), 0, ivec2(1, 0));
cN = texelFetchOffset(uNormData, ivec2(i, 0), 0, ivec2(2, 0));
vec3 uvt;
vec3 intersect;
float z;
bool isHit = hitTriangleSecond(R_.orig, R_.dir, a.xyz, b.xyz, c.xyz, uvt, triangleNormal, intersect, z);
if (isHit) {
if (z<mindist && z > 0.001) {
hitPos1 = intersect;
mindist = z;
weHitSomething = true;
material.type = DIEL;
material.albedo = vec3(.8, .3, .4);
normal = aN.xyz*uvt.x + bN.xyz*uvt.y + cN.xyz*uvt.z;
hitPos = hitPos1;
}
}
}
用于光线命中测试的网格仍然是低分辨率三角形网格,即使您为了校正照明计算而乱用法线,所以阴影中的尖角有点在意料之中。
光线投射依赖于三角形相交,因此要获得平滑的阴影,您需要阴影投射器中的平滑几何体 - 即更多三角形。法线贴图等技术适用于照明,因为您可以根据修改后的方向伪造一个近似的光强度,但实际上并没有任何表面位移会改变物理几何形状,因此这无助于纠正任何不正确的命中测试。
你真正需要的阴影是更多的几何形状,这样你就可以使用真实的三角形准确地描述阴影投射轮廓边缘的形状。