我应该如何在 3D 引擎中实现视图裁剪平面?
How should I be implementing a view clipping plane in a 3D Engine?
这个项目是在 Java 中完全从头开始编写的。自从 Covid 开始以来我就一直很无聊,所以我想要一些可以占用我时间的东西,并教我一些很酷的东西。不过,我已经在这个问题上停留了大约一个星期了。当我尝试使用我的近平面裁剪方法时,它会将新顶点倾斜到屏幕的另一侧,但有时它工作得很好。
Failure Screenshot
Success Screenshot
所以我的想法可能是因为它有时会起作用,我只是没有在管道中的正确时间进行剪辑?
我从面部剔除和光照开始,
然后我对顶点应用相机视图变换,
然后我在近平面上剪辑
最后我应用了投影矩阵并裁剪了所有剩余的屏幕外三角形
代码:
这将计算交点。抱歉,如果它凌乱或太长,我在编码方面不是很有经验,我的专业是物理,而不是 CS。
public Vertex vectorIntersectPlane(Vector3d planePos, Vector3d planeNorm, Vector3d lineStart, Vector3d lineEnd){
float planeDot = planeNorm.dotProduct(planePos);
float startDot = lineStart.dotProduct(planeNorm);
float endDot = lineEnd.dotProduct(planeNorm);
float midPoint = (planeDot - startDot) / (endDot - startDot);
Vector3d lineStartEnd = lineEnd.sub(lineStart);
Vector3d lineToIntersect = lineStartEnd.scale(midPoint);
return new Vertex(lineStart.add(lineToIntersect));
}
public float distanceFromPlane(Vector3d planePos, Vector3d planeNorm, Vector3d vert){
float x = planeNorm.getX() * vert.getX();
float y = planeNorm.getY() * vert.getY();
float z = planeNorm.getZ() * vert.getZ();
return (x + y + z - (planeNorm.dotProduct(planePos)));
}
//When a triangle gets clipped it has 4 possible outcomes
// 1 it doesn't actually need clipping and gets returned
// 2 it gets clipped into 1 new triangle, for testing these are red
// 3 it gets clipped into 2 new triangles, for testing 1 is green, and 1 is blue
// 4 it is outside the view planes and shouldn't be rendered
public void clipTriangles(){
Vector3d planePos = new Vector3d(0, 0, ProjectionMatrix.fNear, 1f);
Vector3d planeNorm = Z_AXIS.clone();
final int length = triangles.size();
for(int i = 0; i < length; i++) {
Triangle t = triangles.get(i);
if(!t.isDraw())
continue;
Vector3d[] insidePoint = new Vector3d[3];
int insidePointCount = 0;
Vector3d[] outsidePoint = new Vector3d[3];
int outsidePointCount = 0;
float d0 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[0]);
float d1 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[1]);
float d2 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[2]);
//Storing distances from plane and counting inside outside points
{
if (d0 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[0];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[0];
outsidePointCount++;
}
if (d1 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[1];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[1];
outsidePointCount++;
}
if (d2 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[2];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[2];
}
}
//Triangle has 1 point still inside view, remove original triangle add new clipped triangle
if (insidePointCount == 1) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[1]);
vertices.add(newVert1);
vertices.add(newVert2);
//Triangles are stored with vertex references instead of the actual vertex object.
Triangle temp = new Triangle(t.getVertKeys()[0], vertices.size() - 2, vertices.size() - 1, vertices);
temp.setColor(1,0,0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
//Triangle has two points inside remove original add two new clipped triangles
if (insidePointCount == 2) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[1], outsidePoint[0]);
vertices.add(newVert1);
vertices.add(newVert2);
Triangle temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 1, vertices);
temp.setColor(0, 1, 0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 2, vertices);
temp.setColor(0, 0, 1, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
}
}
我发现了问题,没有为新的剪切三角形提供正确的顶点引用。他们只是被赋予了三角形的第一个顶点,不管它是否在视图内。
这个项目是在 Java 中完全从头开始编写的。自从 Covid 开始以来我就一直很无聊,所以我想要一些可以占用我时间的东西,并教我一些很酷的东西。不过,我已经在这个问题上停留了大约一个星期了。当我尝试使用我的近平面裁剪方法时,它会将新顶点倾斜到屏幕的另一侧,但有时它工作得很好。
Failure Screenshot
Success Screenshot
所以我的想法可能是因为它有时会起作用,我只是没有在管道中的正确时间进行剪辑?
我从面部剔除和光照开始,
然后我对顶点应用相机视图变换,
然后我在近平面上剪辑
最后我应用了投影矩阵并裁剪了所有剩余的屏幕外三角形
代码:
这将计算交点。抱歉,如果它凌乱或太长,我在编码方面不是很有经验,我的专业是物理,而不是 CS。
public Vertex vectorIntersectPlane(Vector3d planePos, Vector3d planeNorm, Vector3d lineStart, Vector3d lineEnd){
float planeDot = planeNorm.dotProduct(planePos);
float startDot = lineStart.dotProduct(planeNorm);
float endDot = lineEnd.dotProduct(planeNorm);
float midPoint = (planeDot - startDot) / (endDot - startDot);
Vector3d lineStartEnd = lineEnd.sub(lineStart);
Vector3d lineToIntersect = lineStartEnd.scale(midPoint);
return new Vertex(lineStart.add(lineToIntersect));
}
public float distanceFromPlane(Vector3d planePos, Vector3d planeNorm, Vector3d vert){
float x = planeNorm.getX() * vert.getX();
float y = planeNorm.getY() * vert.getY();
float z = planeNorm.getZ() * vert.getZ();
return (x + y + z - (planeNorm.dotProduct(planePos)));
}
//When a triangle gets clipped it has 4 possible outcomes
// 1 it doesn't actually need clipping and gets returned
// 2 it gets clipped into 1 new triangle, for testing these are red
// 3 it gets clipped into 2 new triangles, for testing 1 is green, and 1 is blue
// 4 it is outside the view planes and shouldn't be rendered
public void clipTriangles(){
Vector3d planePos = new Vector3d(0, 0, ProjectionMatrix.fNear, 1f);
Vector3d planeNorm = Z_AXIS.clone();
final int length = triangles.size();
for(int i = 0; i < length; i++) {
Triangle t = triangles.get(i);
if(!t.isDraw())
continue;
Vector3d[] insidePoint = new Vector3d[3];
int insidePointCount = 0;
Vector3d[] outsidePoint = new Vector3d[3];
int outsidePointCount = 0;
float d0 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[0]);
float d1 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[1]);
float d2 = distanceFromPlane(planePos, planeNorm, t.getVerticesVectors()[2]);
//Storing distances from plane and counting inside outside points
{
if (d0 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[0];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[0];
outsidePointCount++;
}
if (d1 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[1];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[1];
outsidePointCount++;
}
if (d2 >= 0){
insidePoint[insidePointCount] = t.getVerticesVectors()[2];
insidePointCount++;
}else{
outsidePoint[outsidePointCount] = t.getVerticesVectors()[2];
}
}
//Triangle has 1 point still inside view, remove original triangle add new clipped triangle
if (insidePointCount == 1) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[1]);
vertices.add(newVert1);
vertices.add(newVert2);
//Triangles are stored with vertex references instead of the actual vertex object.
Triangle temp = new Triangle(t.getVertKeys()[0], vertices.size() - 2, vertices.size() - 1, vertices);
temp.setColor(1,0,0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
//Triangle has two points inside remove original add two new clipped triangles
if (insidePointCount == 2) {
t.dontDraw();
Vertex newVert1 = vectorIntersectPlane(planePos, planeNorm, insidePoint[0], outsidePoint[0]);
Vertex newVert2 = vectorIntersectPlane(planePos, planeNorm, insidePoint[1], outsidePoint[0]);
vertices.add(newVert1);
vertices.add(newVert2);
Triangle temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 1, vertices);
temp.setColor(0, 1, 0, t.getBrightness(), t.getAlpha());
triangles.add(temp);
temp = new Triangle(t.getVertKeys()[0], t.getVertKeys()[1], vertices.size() - 2, vertices);
temp.setColor(0, 0, 1, t.getBrightness(), t.getAlpha());
triangles.add(temp);
continue;
}
}
}
我发现了问题,没有为新的剪切三角形提供正确的顶点引用。他们只是被赋予了三角形的第一个顶点,不管它是否在视图内。