我已经开始了射线追踪之旅。 我设法用射线追踪渲染了一个网格并计算了阴影。但是,当我尝试进行反射并添加反射时,我得到了一个全黑的屏幕。 我已经清理了代码并添加了注释,我想我在计算中发现了问题,但似乎找不到发生这种情况的原因。
#version 300 es\n
precision highp float;
precision highp int;
precision highp sampler2D;
in vec2 vuv;
uniform float time;
uniform vec2 Res, mouse;
uniform sampler2D uMeshData;
uniform int vertsCount;
uniform mat4 uRot;
layout(location = 0) out lowp vec4 fragColor;
#define MAX_BOUNCES 4
#define rmat(a, b) mat3(1, 0, 0, 0, cos(b), -sin(b), 0, sin(b), cos(b)) * mat3(cos(a), 0, sin(a), 0, 1, 0, -sin(a), 0, cos(a))
// Materials
#define LAMB 0
#define METAL 1
#define DIEL 2
struct Ray {
vec3 orig, dir;
}R_;
struct Material
{
int type;
vec3 albedo;
};
struct Sphere{
vec3 center;
float radius;
};
mat4 rotate() {
// original x= mouse.x, y= mouse.y
float x = mouse.y; //y=mouse.y+sin(time*2.),z=0.;
float y= mouse.x,z=0.;
float a = sin(x), b = cos(x), c = sin(y), d = cos(y), e = sin(z), f = cos(z), ac = a * c, bc = b * c;
return mat4(d * f, d * e, -c, 0.0,
ac * f - b * e, ac * e + b * f, a * d, 0.0,
bc * f + a * e, bc * e - a * f, b * d, 0.0,
0.0, 0.0, 0.0, 1.0);
}
mat4 frotate( float x, float y, float z ){
float a = sin(x); float b = cos(x);
float c = sin(y); float d = cos(y);
float e = sin(z); float f = cos(z);
float ac = a*c;
float bc = b*c;
return mat4( d*f, d*e, -c, 0.0,
ac*f-b*e, ac*e+b*f, a*d, 0.0,
bc*f+a*e, bc*e-a*f, b*d, 0.0,
0.0, 0.0, 0.0, 1.0 );
}
mat4 translate( float x, float y, float z ){
return mat4( 1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
x, y, z, 1.0 );
}
vec3 getHitPoint(Ray ray, float t) {
return ray.orig + t * ray.dir;
}
//intersection test with spheres
bool hitSphere(vec3 orig,vec3 dir,vec3 center,float r,out vec3 intersect){
vec3 oc = orig - center;
float b = dot(oc,dir);
float c = dot(oc,oc) - r * r;
if(c>0.0 && b > 0.0) return false;
float discriminant = b*b -c;
if(discriminant < 0.0) return false;
float t= -b-sqrt(discriminant);
if(t<0.0) return false;
intersect = orig + t*dir;
return true;
}
//triangle intersection by miffy
bool hitTriangle(vec3 orig,vec3 dir,vec3 a,vec3 b,vec3 c,out vec3 uvt,out vec3 triangleNormal){
float eps=1e-8;
vec3 ab=b-a;
vec3 ac=c-a;
triangleNormal = normalize(cross(ab,ac));
vec3 n=cross(dir,ac);
float det=dot(ab,n);
// if the determinant is negative the triangle is backfacing
// if the determinant is close to 0, the ray misses the triangl
if(det<=eps){ return false;}
vec3 ao=orig-a;
float u=dot(ao,n)/det;
if(u<0.0 || u>1.0){ return false;}
vec3 e=cross(ao,ab);
float v=dot(dir,e)/det;
if(v<0.0||u+v>1.0){ return false;}
float t= dot(ac,e)/det;
uvt = vec3(u,v,t);
return true;
}
void Camera(out Ray ray, vec3 lookAt, vec3 up, float angle, float aspect) {
vec3 g = normalize(lookAt - ray.orig);
vec3 u = normalize(cross(g, up));
vec3 v = normalize(cross(u, g));
u = u * tan(radians(angle * .5));
v = v * tan(radians(angle * .5)) / aspect;
ray.dir = normalize(g + ray.dir.x * u + ray.dir.y * v);
}
//return intersection point with lightSource for the shadow ray
vec3 hitLightSource(Ray R_, Sphere sphere){
vec3 hit = vec3(0.0,0.0,0.0);
float mindist = -1000.;
vec3 intersect;
bool isHit = hitSphere(R_.orig,R_.dir,sphere.center,sphere.radius,hit);
if(isHit && hit.z > mindist)
{
intersect = hit;
}
return intersect;
}
//return intersection point with the mesh for the shadow ray
vec3 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;
bool isHit = hitTriangle(R_.orig,R_.dir, a.xyz,b.xyz,c.xyz,uvt, triangleNormal);
if (isHit) {
intersect = R_.orig + R_.dir*uvt.z;
float z = intersect.z;
if (z>mindist) {
mindist = z;
}
}
}
return intersect;
}
//this is where if it is shadow we multiply the color with vec3(.4,.4,.4) else with vec3(1.,1.,1.,) so it does not affect the color if its not a shadow
vec3 calcShadow(Sphere lightSource, vec3 hitPos){
vec3 color;
vec3 lightDir = normalize(lightSource.center-hitPos);
Ray shadowRay = Ray(hitPos, lightDir);
vec3 isHitLightDir = hitLightSource(shadowRay,lightSource);
vec3 isHitMesh = hitMesh(shadowRay);
if (isHitMesh.z > isHitLightDir.z ) {
color = vec3(0.4,0.4,0.4);
}else{
color = vec3(1.,1.,1.);
}
return color;
}
//function that it affects the mesh if it is hit by light we multiply with color of the Hitpoint with diffuse
vec3 getLight(vec3 color, Sphere sphere, vec3 intersect, vec3 normal){
vec3 lightDir = normalize(sphere.center-intersect);
float diffuse = clamp(dot(lightDir, normal), 0., 1.);
return color*diffuse;
}
// we check if a ray intersected the scene, if it does we return the hitPos of the inteersection,
// the normal of that hitpoint the material and this bool isSphere if it intersectesd the floor, which is a giant sphere(in order to make it a floor)
bool hitScene(Ray R_, out vec3 hitPos, out vec3 normal, out Material material, Sphere sphere, out bool isShpere, Sphere lightSource){ // na thimithw na thesw to isShpere false stin trace synartisi
vec4 a = vec4(0.0), b = vec4(0.0), c = vec4(0.0);
float mindist = -1000.;
bool weHitSomething = false;
vec3 hitPos1 = vec3(0.);
vec3 triangleNormal = vec3(0.,0.,0.);
vec3 sphereNormal;
//here we chck all the mesh if we hit a triangle if the mesh and we keep the closest hitpoint
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 uvt;
bool isHit = hitTriangle(R_.orig,R_.dir, a.xyz,b.xyz,c.xyz,uvt, triangleNormal);
if (isHit) {
vec3 intersect = R_.orig + R_.dir*uvt.z;
float z = intersect.z;
if (z>mindist) {
hitPos1 = intersect;
mindist = z;
}
}
}
//here we check if it intersected the floor which is a sphere and we keep the position of the hitPos
vec3 hit = vec3(0.);
bool isHit = hitSphere(R_.orig,R_.dir,sphere.center,sphere.radius,hit);
if(isHit && hit.z > mindist)
{
mindist = hit.z;
sphereNormal = (hit - sphere.center) / sphere.radius;
}
//here we have probably have 2 intersection points, one of a sphere, and one of a triangle
// we keep the max of them cause the min dist is actually -1000. so we keep the closest hitpos
float flag = max(hitPos1.z, hit.z);
//based of the type of the interection(triangle or sphere) we assign to the mesh or the floor the material type, the normal and all the
//information Trace function needs
//if we intersected something we set weHitSomething to true
if ((flag == hitPos1.z) && (flag > -1000.)) {
weHitSomething = true;
material.type = METAL;
material.albedo = vec3(0.9, 0.9, 0.9);
normal = triangleNormal;
hitPos = hitPos1;
isShpere = false;
}
else if ((flag == hit.z) && (flag > -1000.)) {
weHitSomething = true;
material.type = LAMB;
material.albedo = vec3(0., 0.9, 0.9);
normal = sphereNormal;
hitPos = hit;
isShpere = true;
}
return weHitSomething;
}
//Trace is the main function of the max bounces
vec3 Trace(out Ray ray, Sphere floor, Sphere lightSource){
vec3 hitPos, normal;
bool isShpere;
Material material;
vec3 color = vec3(0.);
vec3 attenuation = vec3(1.);
vec3 light, shadow;
//this if for every ray to bounce 4 times.(hopefully)
for(int i=0; i< MAX_BOUNCES; i++){
// we check if we hit something
if(hitScene(ray, hitPos, normal, material, floor, isShpere, lightSource)){
//we check the material type(if it is metal it is refering to the mesh)
if (material.type == METAL) {
//we calculate the new direction
vec3 direction = reflect(ray.dir, normal);
//here we check if the new direction and the hitPos normal is >0 then i do all the calculations
//and we start the new ray from the hitPos to the reflected direction
// HERE is the problem for some reason it passes the dot product !!!!!!!
//and it enters in the else where it assigns the color to hitPos !!!!!!!
if (dot(direction,normal) > 0.) {
ray = Ray(hitPos, direction);
light = getLight(color, lightSource,hitPos, normal);
shadow = calcShadow(lightSource, hitPos);
color *= material.albedo * attenuation * light *shadow;
attenuation *= material.albedo;
}
else{
color = hitPos;
}
}
if (material.type == LAMB) {
vec3 direction = reflect(ray.dir, normal);
if (dot(direction,normal) > 0.) {
ray = Ray(hitPos, direction);
light = getLight(color, lightSource,hitPos, normal);
shadow = calcShadow(lightSource, hitPos);
color *= material.albedo * attenuation*light*shadow;
attenuation *= material.albedo;
}
else{
// color = hitPos;
}
}
}
else{
color = attenuation;
}
}
return color;
}
void main() {
//initialize lightSource, floor, Ray, camera
Sphere lightSource = Sphere(vec3(1.,3.,1.), 0.18);
Sphere floor = Sphere(vec3(0., -1e3, 0.), 1e3);
R_ = Ray(vec3(0.0, 1.0, 6.0), vec3(vuv, -1.));
Camera(R_, vec3(0., 0., 1.), vec3(0., 1., 0.), 90.0, (Res.x / Res.y));
// rotation
R_.dir = mat3(uRot) * R_.dir;
R_.orig = mat3(uRot) * R_.orig;
//color coming from the trace function
vec3 color = Trace(R_, floor, lightSource);
fragColor.rgb = color;
fragColor.a = 1.0;
}
我想知道我是否使用 hitScene 和 Trace 函数从根本上错了。三角形相交和灯光在没有递归的情况下也能正常工作(当我在主函数中都拥有它们时)。
在我的hitScene函数中,我检查了两个交叉点。一个是地板,它是一个巨大的球体,另一个是网格的其余部分,我返回击中位置,击中位置和击中材料的法线。然后,将其返回到跟踪功能。
在此基础上,我计算了新的反射射线并应用了这些材料。显然有些事情完全错了,我希望我能提示从哪里开始寻找。