我正在GLSL 4.0中编写一个简单的光线跟踪器,我在使用正确的着色(phong)进行基本反射方面遇到了一些麻烦。我正在使用渲染到纹理着色器(屏幕对齐四边形)编写我的场景由6个球体和一个平面组成,如下面的代码所述。我使用Intersection
结构来跟踪交叉点和光线的Ray
结构。由于GLSL 4.0不允许递归,我迭代地投射二次光线(通过每次迭代改变每条光线的方向和原点)。
#version 400
in vec3 dir;
out vec4 outcolour;
uniform mat4 mMatrix;
uniform mat4 mvMatrix;
uniform mat4 mvMatrixScene;
uniform mat4 pMatrix;
uniform mat3 normalMatrix; //mv matrix without translation
uniform vec3 light = vec3(6,4,3);
uniform vec4 ambient = vec4(0.0,0.0,0.0,1.0);
uniform vec4 diffuse = vec4(1.0,0.1,0.0,1.0);
uniform vec4 specular = vec4(1.0,1.0,1.0,1.0);
uniform float ambientCoefficent = 1.0;
uniform float diffuseCoefficent = 1.0;
uniform float specularCoefficent = 1.0;
const float PI = 3.14159265358979;
const int raytraceDepth = 2;
const int numSpheres = 6;
//example data structures
struct Ray
{
vec3 origin;
vec3 dir;
};
struct Sphere
{
vec3 centre;
float radius;
vec3 colour;
};
struct Plane
{
vec3 point;
vec3 normal;
vec3 colour;
};
struct Intersection
{
float t; //closest hit
vec3 point; // hit point
vec3 normal; // normal
int hit; //did it hit?
vec3 colour; // colour accumulation, can be also implemented in struct Ray
};
void sphere_intersect(Sphere sph, Ray ray, inout Intersection intersect) {
vec3 dp = ray.origin - sph.centre;
vec3 d = ray.dir;
float r = sph.radius;
// Does the sphere intersect?
float x = pow(dot(d,dp),2.0) - pow(length(dp),2.0) + pow(r,2.0);
intersect.hit = 0;
if (x >= 0) {
float u1 = dot(-d,dp) + sqrt(x);
float u2 = dot(-d,dp) - sqrt(x);
float u = min(u1,u2);
if (u < intersect.t) {
intersect.t = u;
intersect.colour = sph.colour;
intersect.point = ray.origin + intersect.t*d;
intersect.hit = 1;
intersect.normal = (intersect.point - sph.centre) / sph.radius;
}
}
}
void plane_intersect(Plane pl, Ray ray, inout Intersection intersect)
{
float x = dot(ray.dir,pl.normal);
// Assuming no paralel rays otherwise divide by 0 error
float h = dot(pl.point - ray.origin,pl.normal)/x;
intersect.hit = 0;
//intersect.colour = vec3(0.0,0.0,0.0);
intersect.point = vec3(0.0,0.0,0.0);
if (h >=0 ) {
float u = -dot((ray.origin - pl.point),pl.normal)/x;
if (u < intersect.t) {
intersect.t = u;
intersect.point = ray.origin + u*ray.dir;
intersect.normal = pl.normal;
intersect.hit = 1;
// Normal used is y so used x and z for checkerboard pattern
if (int((floor(intersect.point.x) + floor(intersect.point.z)))%2 == 1) {
intersect.colour = pl.colour;
} else {
intersect.colour = vec3(0.0,0.0,0.0);
}
}
}
}
Sphere sphere[numSpheres];
Plane plane;
vec4 local_shading_term(vec3 light, Intersection intersection) {
float q = 10;
float d = length(light - intersection.point);
float s = 10;
vec3 n = normalize(intersection.normal);
vec3 l = normalize(light - intersection.point);
vec3 r = normalize(-reflect(l,n));
vec3 v = -normalize(intersection.point);
vec4 diff = diffuse * diffuseCoefficent * vec4(max(dot(n,l),0));
vec4 spec = specular * specularCoefficent * vec4(pow(max(dot(v,r),0), q));
return (diff + spec) * 250 / (4 * PI * (d + s));
}
void Intersect(Ray ray, inout Intersection intersection)
{
vec3 colour = vec3(0.0,0.0,0.0);
int sphere_hits = 0;
// Sphere intersection
for (int i=0; i<numSpheres; i++) {
sphere_intersect(sphere[i],ray,intersection);
sphere_hits += intersection.hit;
}
if (sphere_hits > 0) {
colour = intersection.colour + vec3(local_shading_term(light,intersection));
}
// Plane intersection
plane_intersect(plane,ray,intersection);
if (sphere_hits == 0 ) {
colour = intersection.colour;
}
intersection.colour = colour;
}
void main()
{
//White
sphere[0].centre = vec3(-2.0, 1.5, -3.5);
sphere[0].radius = 1.5;
sphere[0].colour = vec3(0.8,0.8,0.8);
//Green
sphere[1].centre = vec3(-0.5, 0.0, -2.0);
sphere[1].radius = 0.6;
sphere[1].colour = vec3(0.3,0.8,0.3);
//Blue
sphere[2].centre = vec3(1.0, 0.7, -2.2);
sphere[2].radius = 0.8;
sphere[2].colour = vec3(0.3,0.8,0.8);
//Yellow
sphere[3].centre = vec3(0.7, -0.3, -1.2);
sphere[3].radius = 0.2;
sphere[3].colour = vec3(0.8,0.8,0.3);
//Red
sphere[4].centre = vec3(-0.7, -0.3, -1.2);
sphere[4].radius = 0.2;
sphere[4].colour = vec3(0.8,0.3,0.3);
//Purple
sphere[5].centre = vec3(0.2, -0.2, -1.2);
sphere[5].radius = 0.3;
sphere[5].colour = vec3(0.8,0.3,0.8);
plane.point = vec3(0,-0.5, 0);
plane.normal = vec3(0, 1.0, 0);
plane.colour = vec3(1, 1, 1);
vec4 colour = vec4(0,0,0,1);
Ray ray;
ray.origin = vec3(0.0,0.0,0.0);
ray.dir = normalize(dir);
Intersection intersection;
for (int i=0; i<raytraceDepth; i++) {
intersection.t = 999999999;
Intersect(ray,intersection);
ray.origin = intersection.point;
ray.dir = reflect(ray.dir,intersection.normal);
}
colour = vec4(intersection.colour,0.0);
outcolour = colour;
}
我已经调试了几天我的代码,但我似乎无法实现我的目标,最好用下图来描述:
除了阴影和精度问题,我试图实现上述目标,但现在它看起来像这样:
raytraceDepth = 1
如您所见,平面上方的背景中存在随机垃圾(可能是由于我未正确设置颜色值所致,因此它使用GPU内存中的任何内容)。当我加载着色器时,有时会出现噪音,有时它不是,当我拍摄这些截图时,恰好就在那里。当intersection.point
中的plane_intersect
设置为vec3(0.0,0.0,0.0)
时,球体会产生奇怪的镜像效果。如果我将它移除,我不再看到效果了,但是更大的球体内的实际球体,这就是我保留它的原因。
我的想法是,我将光线与场景中的所有物体相交。交集结构作为inout
参数传递给函数Intersect()
,该函数测试与光线的交叉点。它使用最近的可见交叉点的值填充结构。因此t
表示用于确定发生交叉的光线上的点的值,即ray = ray_origin + t*ray_direction
。
我不确定为什么我无法获得正确的反射效果。任何人都可以看到我的代码有任何问题?