bool FrontEndCullingByArea(VSOutput v0, VSOutput v1, VSOutput v2) { if (context.frontEndCull == FrontEndCull.Off) { return(true); } if (context.cullMode == CullMode.None) { return(true); } //cull back Vector2 p0 = new Vector2(v0.position.X, v0.position.Y); Vector2 p1 = new Vector2(v1.position.X, v1.position.Y); Vector2 p2 = new Vector2(v2.position.X, v2.position.Y); //做了透射除法,Culling准确点 p0 *= 1 / v0.position.W; p1 *= 1 / v1.position.W; p2 *= 1 / v2.position.W; float area = Rasterizer.EdgeFunction(p0, p1, p2); //在Clip Space, area >= 0表示顺时针 if (context.winding == Winding.Clockwise) { return(area > 0); } return(area < 0); }
public static VSOutput PerspectiveDivide(VSOutput vClip) { const float FLT_EPSILON = 1.192092896e-07F; // smallest such that 1.0+FLT_EPSILON != 1.0 if (vClip.position.W <= FLT_EPSILON) { return(null); } VSOutput vNDC = new VSOutput(); Vector4 position = vClip.position; float fInvW = 1 / position.W; position = new Vector4(position.X * fInvW, position.Y * fInvW, position.Z * fInvW, fInvW); vNDC.position = position; vNDC.color = vClip.color * fInvW; vNDC.texcoord = vClip.texcoord * fInvW; vNDC.normalWorld = vClip.normalWorld * fInvW; return(vNDC); }
public void DrawFace(WavefrontObject meshObj, WavefrontFace face, Shader shader) { //WavefontFace是一个triangle fan //可能会有多个三角形;这些三角形共用第一个顶点做v0 if (face.Vertices.Count < 3) { Debug.Assert(false); return; } Vertex vertex0 = Vertex.FromWavefrontVertex(meshObj, face.Vertices[0]); VSOutput vClip0 = shader.VertShader(vertex0); context.statics.vertexCount += 1; for (int i = 1; i + 1 < face.Vertices.Count; i += 1) { Vertex vertex1 = Vertex.FromWavefrontVertex(meshObj, face.Vertices[i]); Vertex vertex2 = Vertex.FromWavefrontVertex(meshObj, face.Vertices[i + 1]); //顶点着色器 VSOutput vClip1 = shader.VertShader(vertex1); VSOutput vClip2 = shader.VertShader(vertex2); context.statics.vertexCount += 2; DrawTriangle(vClip0, vClip1, vClip2); } }
static VSOutput ComputeIntersection(VSOutput S, VSOutput E, float ds, float de) { float amount = ds / (ds - de); VSOutput v = VSOutput.Lerp(S, E, amount); return(v); }
static List <VSOutput> ClipToPlane(List <VSOutput> inputs, Vector4 plane) { List <VSOutput> outputs = new List <VSOutput>(); VSOutput S = inputs.Last(); float ds = Vector4.Dot(plane, S.position); bool sInside = ds >= 0; foreach (var E in inputs) { float de = Vector4.Dot(plane, E.position); bool eInside = de >= 0; if (eInside) { if (!sInside) { VSOutput C = ComputeIntersection(S, E, ds, de); outputs.Add(C); } outputs.Add(E); } else if (sInside) { VSOutput C = ComputeIntersection(S, E, ds, de); outputs.Add(C); } //记录为上一个 S = E; ds = de; sInside = eInside; } return(outputs); }
} //viewport坐标 //Other Attributes public void Clone(VSOutput other) { this.position = other.position; this.normalWorld = other.normalWorld; this.texcoord = other.texcoord; this.color = other.color; this.posScreen = other.posScreen; }
void DrawWireframe(VSOutput v0, VSOutput v1, VSOutput v2) { Color col = context.wireframeColor; //col = Utils.NextWireframeColor(); DrawLine(v0, v1, col); DrawLine(v1, v2, col); DrawLine(v2, v0, col); }
//裁剪前 public void DrawTriangle(VSOutput vClip0, VSOutput vClip1, VSOutput vClip2) { //Backface Culling if (!FrontEndCulling(vClip0, vClip1, vClip2)) { return; } context.statics.triangleCount += 1; //Clipping List <VSOutput> inputs = new List <VSOutput> { vClip0, vClip1, vClip2 }; List <VSOutput> outputs = Clipping.Clip(inputs, context); //输出结果是trianglefan if (outputs.Count < 3) { //全裁剪掉了 return; } //画Triangle Fan VSOutput v0 = outputs[0]; VSOutput vNDC0 = Rasterizer.PerspectiveDivide(v0); if (vNDC0 == null) { return; } VSOutput vScreen0 = rasterizer.ViewportTransform(vNDC0); for (int i = 1; i + 1 < outputs.Count; i += 1) { VSOutput v1 = outputs[i]; VSOutput v2 = outputs[i + 1]; VSOutput vNDC1 = Rasterizer.PerspectiveDivide(v1); VSOutput vNDC2 = Rasterizer.PerspectiveDivide(v2); if (vNDC1 == null || vNDC2 == null) { continue; } VSOutput vScreen1 = rasterizer.ViewportTransform(vNDC1); VSOutput vScreen2 = rasterizer.ViewportTransform(vNDC2); //检查Backface if (i == 1) { //由于裁剪前后的三角形都在同一个平面上,所以只要检查第一个就够了 if (!BackfaceCulling(vScreen0, vScreen1, vScreen2)) { return; } } context.statics.rasterTriCount += 1; rasterizer.RasterizeTriangle(vScreen0, vScreen1, vScreen2); } }
public static VSOutput Lerp(VSOutput A, VSOutput B, float amount) { VSOutput C = new VSOutput(); C.position = Vector4.Lerp(A.position, B.position, amount); C.normalWorld = Vector3.Lerp(A.normalWorld, B.normalWorld, amount); C.texcoord = Vector2.Lerp(A.texcoord, B.texcoord, amount); C.color = Vector4.Lerp(A.color, B.color, amount); C.posScreen = Vector3.Lerp(A.posScreen, B.posScreen, amount); return(C); }
public void Test_BarycentricRasterizeTriangle() { context.clearColor = Color.White; Clear(); //假设n = 1, far = 2 //测试光栅化和插值 //第一个三角形,v0红色在中上,v1绿和v2蓝在中间的左右,v0在far plane, v1,v2在near plane VSOutput v0 = new VSOutput(); v0.position = new Vector4(0f, 1f, 1f, 2f); v0.color = new Vector4(1f, 0f, 0f, 1f); VSOutput v1 = new VSOutput(); v1.position = new Vector4(-0.5f, -0.5f, -1f, 1f); v1.color = new Vector4(0f, 1f, 0f, 1f); VSOutput v2 = new VSOutput(); v2.position = new Vector4(0.5f, -0.5f, -1f, 1f); v2.color = new Vector4(0f, 0f, 1f, 1f); v0 = Rasterizer.PerspectiveDivide(v0); v0 = rasterizer.ViewportTransform(v0); v1 = Rasterizer.PerspectiveDivide(v1); v1 = rasterizer.ViewportTransform(v1); v2 = Rasterizer.PerspectiveDivide(v2); v2 = rasterizer.ViewportTransform(v2); rasterizer.BarycentricRasterizeTriangle(v0, v1, v2); //测试depth test //第二个三角形,v0红在近平面,v1,v2在远 v0.position = new Vector4(-0.5f, 0.5f, -1f, 1f); v1.position = new Vector4(-2f, -1f, 1f, 2f); v2.position = new Vector4(0f, -2f, 1f, 2f); v0 = Rasterizer.PerspectiveDivide(v0); v0 = rasterizer.ViewportTransform(v0); v1 = Rasterizer.PerspectiveDivide(v1); v1 = rasterizer.ViewportTransform(v1); v2 = Rasterizer.PerspectiveDivide(v2); v2 = rasterizer.ViewportTransform(v2); rasterizer.BarycentricRasterizeTriangle(v0, v1, v2); //第三个三角形,v1绿在近,其他在远平面 v0.position = new Vector4(-2f, 0f, 1f, 2f); v1.position = new Vector4(-0.5f, -1f, -1f, 1f); v2.position = new Vector4(0f, -1f, 1f, 2f); v0 = Rasterizer.PerspectiveDivide(v0); v0 = rasterizer.ViewportTransform(v0); v1 = Rasterizer.PerspectiveDivide(v1); v1 = rasterizer.ViewportTransform(v1); v2 = Rasterizer.PerspectiveDivide(v2); v2 = rasterizer.ViewportTransform(v2); rasterizer.BarycentricRasterizeTriangle(v0, v1, v2); }
public void DrawLine(VSOutput v0, VSOutput v1, Color color) { Vector2 p0 = new Vector2(v0.posScreen.X, v0.posScreen.Y); Vector2 p1 = new Vector2(v1.posScreen.X, v1.posScreen.Y); bool accept = CohenSutherlandLineClip(ref p0, ref p1); if (!accept) { return; } BresenhamDrawLine(p0, p1, color); }
public VSOutput VertShader(Vertex v) { VSOutput OUT = new VSOutput(); OUT.position = Vector4.Transform(v.position, MVP); Vector4 normalWorld = Vector4.Transform(v.normal, Matrix4x4.Transpose(worldToModel)); OUT.normalWorld = new Vector3(normalWorld.X, normalWorld.Y, normalWorld.Z); OUT.texcoord = v.texcoord; OUT.color = v.color; return(OUT); }
public void RasterizeTriangle(VSOutput vScreen0, VSOutput vScreen1, VSOutput vScreen2) { switch (context.drawMode) { case DrawMode.Normal: case DrawMode.Depth: BarycentricRasterizeTriangle(vScreen0, vScreen1, vScreen2); break; case DrawMode.Wireframe: DrawWireframe(vScreen0, vScreen1, vScreen2); break; } }
public VSOutput ViewportTransform(VSOutput vNDC) { VSOutput vScreen = new VSOutput(); vScreen.Clone(vNDC); int width = context.frameBuffer.Width; int height = context.frameBuffer.Height; float x = (vNDC.position.X + 1) * 0.5f; float y = (1 - vNDC.position.Y) * 0.5f; //把z从[-1,1]转到[0,1] float z = (vNDC.position.Z + 1) * 0.5f; vScreen.posScreen = new Vector3(x * width, y * height, z); return(vScreen); }
public void TestClipping() { Clear(); context.clippingMode = ClippingMode.SixPlane; //context.clippingMode = ClippingMode.Off; context.drawMode = DrawMode.Wireframe; context.cullMode = CullMode.Back; context.frontEndCull = FrontEndCull.Off; VSOutput v0 = new VSOutput(); VSOutput v1 = new VSOutput(); VSOutput v2 = new VSOutput(); List <VSOutput> inputs = new List <VSOutput>() { v0, v1, v2 }; //第一个三角形,右裁剪平面,一个顶点在外=>生成两个三角形 v0.position = new Vector4(0.5f, 0.5f, 0, 1); v1.position = new Vector4(0, -0.5f, 0, 1); v2.position = new Vector4(2, 0, 0, 1);//在外面 DrawTriangle(v0, v1, v2); //第二个三角形,上裁剪平面,两个顶点在外=>生成一个三角形 v0.position = new Vector4(-0.5f, 4f, 0, 1); v1.position = new Vector4(0.5f, 0f, 0, 1); //在外面 v2.position = new Vector4(0.6f, 2f, 0, 1); //在外面 DrawTriangle(v0, v1, v2); //第三个,三个顶点都在外 v0.position = new Vector4(0.6f, 1.5f, 0, 1); v1.position = new Vector4(-1.5f, -0.3f, 0, 1); v2.position = new Vector4(0.8f, -1.2f, 0, 1); DrawTriangle(v0, v1, v2); //顺别测下Back face Culling;对换了v1和v2,应该不画 v0.position = new Vector4(0.6f, 1.5f, 0, 1); v2.position = new Vector4(-1.5f, -0.3f, 0, 1); v1.position = new Vector4(0.8f, -1.2f, 0, 1); //DrawTriangle(v0, v1, v2); Present(); }
public PSOutput FragShader(VSOutput v) { //invert uv if (shaderContext.invertTexture) { //v.texcoord = new Vector2(1 - v.texcoord.X, 1 - v.texcoord.Y); v.texcoord = new Vector2(v.texcoord.X, 1 - v.texcoord.Y); //v.texcoord = new Vector2(1 - v.texcoord.X, v.texcoord.Y); } PSOutput OUT = new PSOutput(); Vector4 col = v.color; if ((shaderContext.shadeMode == ShadeMode.Lighting || shaderContext.shadeMode == ShadeMode.NDotL) && shaderContext.light != null) { Vector3 normalWorld = v.normalWorld; normalWorld = Vector3.Normalize(normalWorld); Vector3 lightDir = shaderContext.light.lightDirForShader; //Lambert Lighting float NDotL = Vector3.Dot(normalWorld, lightDir); NDotL = Utils.Clamp(NDotL, 0, 1); if (shaderContext.shadeMode == ShadeMode.Lighting && texture != null) { Vector4 tex = texture.Tex2D(v.texcoord, shaderContext.textureFilterMode); col = tex * (NDotL * shaderContext.light.lightColor + shaderContext.ambient); } else { col = new Vector4(NDotL, NDotL, NDotL, 1f); } } else if (shaderContext.shadeMode == ShadeMode.Texture && texture != null) { col = texture.Tex2D(v.texcoord, shaderContext.textureFilterMode); } //col = new Vector4(v.texcoord.X, v.texcoord.Y, 0, 1 ); OUT.color = col; return(OUT); }
bool FrontEndCullingByAngle(VSOutput v0, VSOutput v1, VSOutput v2) { if (context.frontEndCull == FrontEndCull.Off) { return(true); } if (context.cullMode == CullMode.None) { return(true); } Vector3 pos0 = new Vector3(v0.position.X, v0.position.Y, v0.position.Z); Vector3 pos1 = new Vector3(v1.position.X, v1.position.Y, v1.position.Z); Vector3 pos2 = new Vector3(v2.position.X, v2.position.Y, v2.position.Z); //为什么把Z改成Eye Space的Z就是对的? //pos0.Z = v0.position.W; //pos1.Z = v1.position.W; //pos2.Z = v2.position.W; //Vector3 n = Vector3.Cross(pos1-pos0, pos2 - pos0); Vector3 n = Vector3.Cross(pos1 - pos0, pos2 - pos1); Vector3 p = pos0; Vector3 test = Vector3.Cross(new Vector3(1, 0, 0), new Vector3(0, 1, 0)); //n = Vector3.Normalize(n); //p = Vector3.Normalize(p); float cos = Vector3.Dot(n, p); if (context.winding == Winding.Clockwise) { return(cos <= 0); } return(cos >= 0); }
bool BackfaceCulling(VSOutput vScreen0, VSOutput vScreen1, VSOutput vScreen2) { if (context.cullMode == CullMode.None) { return(true); } if (context.frontEndCull == FrontEndCull.On) { return(true); } Vector2 p0 = new Vector2(vScreen0.posScreen.X, vScreen0.posScreen.Y); Vector2 p1 = new Vector2(vScreen1.posScreen.X, vScreen1.posScreen.Y); Vector2 p2 = new Vector2(vScreen2.posScreen.X, vScreen2.posScreen.Y); float area = Rasterizer.EdgeFunction(p0, p1, p2); if (context.winding == Winding.Clockwise) { return(area < 0); } //在Viewport Space, 由于Y轴向下, area > 0表示逆时针 return(area > 0); }
public void BarycentricRasterizeTriangle(VSOutput v0, VSOutput v1, VSOutput v2) { int width = context.frameSize.Width; int height = context.frameSize.Height; Vector2 xy0 = new Vector2(v0.posScreen.X, v0.posScreen.Y); Vector2 xy1 = new Vector2(v1.posScreen.X, v1.posScreen.Y); Vector2 xy2 = new Vector2(v2.posScreen.X, v2.posScreen.Y); float z0 = v0.posScreen.Z; float z1 = v1.posScreen.Z; float z2 = v2.posScreen.Z; //求包围盒 float xmin = Math.Min(Math.Min(xy0.X, xy1.X), xy2.X); float xmax = Math.Max(Math.Max(xy0.X, xy1.X), xy2.X); float ymin = Math.Min(Math.Min(xy0.Y, xy1.Y), xy2.Y); float ymax = Math.Max(Math.Max(xy0.Y, xy1.Y), xy2.Y); //像素坐标包围盒 int x0 = Utils.Clamp((int)xmin, 0, width - 1); int y0 = Utils.Clamp((int)ymin, 0, height - 1); int x1 = Utils.Clamp((int)xmax, 0, width - 1); int y1 = Utils.Clamp((int)ymax, 0, height - 1); for (int y = y0; y <= y1; ++y) { for (int x = x0; x <= x1; ++x) { //加0.5取像素的中间位置的坐标 Vector2 pixel = new Vector2(x + 0.5f, y + 0.5f); Vector3 w = BarycentricCoordinates(pixel, xy0, xy1, xy2); float w0 = w.X; float w1 = w.Y; float w2 = w.Z; if (!(w0 >= 0 && w1 >= 0 && w2 >= 0)) { //像素点不在三角形内 continue; } context.statics.fragmentCount += 1; //深度测试 float depth = w0 * z0 + w1 * z1 + w2 * z2; if (context.depthBuffer[x, y] < depth) { continue; } Color col; if (context.drawMode == DrawMode.Depth) { Vector4 depthColor = new Vector4(depth, depth, depth, 1); col = Utils.VectorToColor(depthColor); } else { //正常渲染 //插值UV等顶点属性 float fInvW = w0 * v0.position.W + w1 * v1.position.W + w2 * v2.position.W; //由于position.W是1/W = 1/-Z_Eye float Z_Eye = 1f / fInvW; Vector2 uv = w0 * v0.texcoord + w1 * v1.texcoord + w2 * v2.texcoord; uv *= Z_Eye; Vector4 color = w0 * v0.color + w1 * v1.color + w2 * v2.color; color *= Z_Eye; Vector3 normalWorld = w0 * v0.normalWorld + w1 * v1.normalWorld + w2 * v2.normalWorld; if (shader != null) { VSOutput fragment = new VSOutput(); //v.position = fragment.normalWorld = normalWorld; fragment.texcoord = uv; fragment.color = color; PSOutput OUT = shader.FragShader(fragment); if (OUT.isDiscard) { continue; } col = Utils.VectorToColor(OUT.color); } else { col = Utils.VectorToColor(color); } } context.depthBuffer[x, y] = depth; DrawPixel(x, y, col); } } }
bool FrontEndCulling(VSOutput v0, VSOutput v1, VSOutput v2) { return(FrontEndCullingByArea(v0, v1, v2)); //return FrontEndCullingByAngle(v0, v1, v2); }