我正在尝试为研究目的实现梯度域路径跟踪器。为了实现这一步,我首先需要一个工作路径跟踪器。到目前为止我一直在创建一个,但结果是错误的,我会解释你为什么。一些概念:
我正在处理在采样之前生成的路径。我的意思是我的算法的第一步是计算某个像素(x,y)的路径。此路径将在场景中执行一些反弹,如果终止在灯光上,则视为有效。
首先:以下是结构的定义(将在稍后声明和初始化),包含将光线从相机投射到场景中所需的一些场景信息。
struct RenderData
{
vec3 E;
vec3 p1;
vec3 dx;
vec3 dy;
};
它在InitializeScene方法中初始化:
void InitializeScene(){ // setup virtual screen plane
vec3 E( 2, 8, -26 ); //Eye position
vec3 V( 0, 0, 1 ); //LookAt vector
float d = 0.5f, ratio = SCRWIDTH / SCRHEIGHT, focal = 18.0f;
vec3 p1(E + V * focal + vec3(-d * ratio * focal, d * focal, 0)); // top-left screen corner in SCREEN SPACE
vec3 p2(E + V * focal + vec3(d * ratio * focal, d * focal, 0)); // top-right screen corner
vec3 p3(E + V * focal + vec3(-d * ratio * focal, -d * focal, 0)); // bottom-left screen corner
mat4 M = rotate( mat4( 1 ), r, vec3( 0, 1, 0 ) );
p1 = vec3(M * vec4(p1, 1.0f)); //rotating the above points
p2 = vec3(M * vec4(p2, 1.0f));
p3 = vec3(M * vec4(p3, 1.0f));
renderData.dx = (p2 - p1) / (float)SCRWIDTH;
renderData.dy = (p3 - p1) / (float)SCRHEIGHT;
renderData.E = vec3(M * vec4(E, 1.0f));
renderData.p1 = p1;
}
上面的代码是为了让您了解我如何初始化场景。 我也有结构来存储关于我的路径的信息:
struct PathVert {
vec3 p; vec3 n; //hit point and normal of the surface hit
};
struct Path {
PathVert verts[MAX_DEPTH]; //maxDepth is 15 for now
int vertCount;
int x, y; //which pixel this path is referring to
};
因此我开始考虑逐像素。
for (int y = 0; y < SCRHEIGHT; y++) for (int x = 0; x < SCRWIDTH; x++)
{
Path path;
if(generatePath(x,y, path)){
Sample(path);
}
}
generatePath()方法确实跟踪进入场景的路径并检查它命中的所有顶点。您将看到的 checkIfRayIntersectSomething(t)方法,它只是在我的框架中实现的伪方法,并且我省略了其长度的发布原因。我用它来检查我的光线是否在场景中遇到某些东西,如果是,它会用距离该对象的距离更新“t”。注意:灯不被视为物体本身。因此,我还有一个 checkRayLightIntersection(hitLightPoint),用于检查与灯光的交点,如果有的话,hitLightPoint会更新我正在点击的灯光上的点。 灯光是2D表面。
Vec lightPos = Vec(5, 15, 2); //hard coded position of the light
如上所述,光是一个表面,但恰好是一个正方形表面,其4个角度为:
Vec P1 = Vec(lightPos.x - 20, lightPos.y, lightPos.z + 20);
Vec P2 = Vec(lightPos.x + 20, lightPos.y, lightPos.z + 20);
Vec P3 = Vec(lightPos.x + 20, lightPos.y, lightPos.z - 20);
Vec P4 = Vec(lightPos.x - 20, lightPos.y, lightPos.z - 20);
相当大,我知道,所以第一个问题依赖于这个方面,它是否正确有这么大的亮点?
但是让我们来看看主要方法。在此您可以看到GeneratePath方法:
bool GeneratePath(int x, int y, Path &path){
path.x = x;
path.y = y;
path.vertCount = 0;
vec3 P = renderData.p1 + renderData.dx * ((float)(x) + Rand(1)) + renderData.dy * ((float)(y) + Rand(1));
vec3 O = renderData.E + vec3(Rand(0.4f) - 0.2f, Rand(0.4f) - 0.2f, Rand(0.4f) - 0.2f);
vec3 D = normalize(P - O); //direction of the first ray, the one from the camera towards the pixel we are considering
for (int depth = 1; depth <= MAXDEPTH; depth++){
float t;
Vec hitLightPoint;
PathVert vert;
if (!checkIfRayIntersectSomething(t)){
//we didn't find any object.. but we still may have found the light which is an object non represented in the scene
//the depth check avoids me rendering the light as a white plane
if (depth > 1 && checkRayLightIntersection(O, D, hitLightPoint)){
//update the vertex since we realized it's the light
vert.p = hitLightPoint;
vert.n = Vec(0, -1, 0);//cause the light is pointing down
path.verts[depth - 1] = vert;
path.vertCount++;
return true; //light hit, path completed
}
return false; //nothing hit, path non valid
}
//otherwise I got a hit into the scene
vert.p = O + D * t; //reach the hitPoint
vert.n = methodToFindTheNormal();
vert.color = CalculateColor(vert.p); //according to the material properties (only diffuse objects so far)
path.verts[depth - 1] = vert;
path.vertCount++;
//since I have the light, and a path terminates when it hits the light, I have to check out also if my ray hits this light,
//and if does, I have to check whether it first hits the light or the object just calculated above
//moreover with the "depth > 1" check, I avoid again rendering the light which otherwise would be visible as a white plane
if (depth > 1 && checkRayLightIntersection(O, D, hitLightPoint)){
float distFromObj = length(vert.p);
float distFromLight = length(hitLightPoint);
if (distFromLight < distFromObj){
//update the vertex since we realized it's the light
vert.p = hitLightPoint;
vert.n = Vec(0, -1, 0);
vert.color = Vec(1, 1, 1);// TODO light color? or light emission?
path.verts[depth - 1] = vert;
return true; //light hit, path completed
}
}
if (depth == MAXDEPTH) return false;
Vec newDir = BSDFDiffuseReflectionCosineWeighted(vert.n, D);//explained later
D = newDir;
O = vert.p;
}
return false;
}
BSDFDiffuseReflectionCosineWeighted()只计算新方向,测试和工作。最后剩下的是Sample方法,它计算像素的最终颜色。
Vec Sampling(Path &path){
Vec color(1, 1, 1);
for (int vert = 0; vert < path.vertCount - 1; vert++) { //considers the last vertex as the light
const PathVert &currVert = path.verts[vert];
const PathVert &nextVert = path.verts[vert + 1];
Vec wo = (nextVert.p - currVert.p).norm();
double cosTheta = fabs(wo.dot(currVert.n));
float PDF = cosTheta/PI;
if (cosTheta <= 1e-6) return Vec();
//considering only DIFFUSE objects
color = color.mult(currVert.color * (cosTheta / M_PI) / PDF);
}
return color.mult(Vec(10.0f, 10.0f, 10.0f)); //multiplication for the light emission?
}
16SPP的结果是:
正如你所看到的结果并不那么糟糕,但有一个主要问题:缺少阴影..试过很多组合但没有改进。算法本身存在一些错误。你能帮我理解为什么吗?提前谢谢。
编辑:这是我的目标参考: