private void CreateShader() { m_BlinnPhongShader = new BlinnPhongShader(); }
private void RasterTriangle(Vertex v0, Vertex v1, Vertex v2) { // 模型变换:记录世界空间的坐标,计算光照时使用 Vector3 v0_world = m_Model.MultiplyPoint(v0.position); Vector3 v1_world = m_Model.MultiplyPoint(v1.position); Vector3 v2_world = m_Model.MultiplyPoint(v2.position); // View变换:记录观察空间中的深度,Unity中向量和矩阵相乘直接就变换成三个分量的向量了 Vector3 v0_view = m_View.MultiplyPoint(v0_world); Vector3 v1_view = m_View.MultiplyPoint(v1_world); Vector3 v2_view = m_View.MultiplyPoint(v2_world); // 投影变换 Vector3 v0_NDC = m_Projection.MultiplyPoint(v0_view); Vector3 v1_NDC = m_Projection.MultiplyPoint(v1_view); Vector3 v2_NDC = m_Projection.MultiplyPoint(v2_view); // Clip if (v0_NDC.x < -1 || v0_NDC.x > 1 || v0_NDC.y < -1 || v0_NDC.y > 1 || v0_NDC.z < -1 || v0_NDC.z > 1 || v1_NDC.x < -1 || v1_NDC.x > 1 || v1_NDC.y < -1 || v1_NDC.y > 1 || v1_NDC.z < -1 || v1_NDC.z > 1 || v2_NDC.x < -1 || v2_NDC.x > 1 || v2_NDC.y < -1 || v2_NDC.y > 1 || v2_NDC.z < -1 || v2_NDC.z > 1) { return; } // Cull,Unity中三角形顺时针为正面,向量叉乘的方向在右手坐标系中使用右手定则确定,在左手坐标系中使用左手定则确定。 // 这里需要注意不管是在右手坐标系还是左手坐标系,叉乘向量的数值结果是一样的,只不过表示的绝对向量方向不同,Unity中是左手坐标系,遵循左手定则。 if (m_CullType == CullType.Back) { Vector3 v0v1 = v1_NDC - v0_NDC; Vector3 v0v2 = v2_NDC - v0_NDC; if (Vector3.Cross(v0v1, v0v2).z > 0) { return; } } if (m_CullType == CullType.Front) { Vector3 v0v1 = v1_NDC - v0_NDC; Vector3 v0v2 = v2_NDC - v0_NDC; if (Vector3.Cross(v0v1, v0v2).z < 0) { return; } } // Viewport Vector3 v0_screen = new Vector3((v0_NDC.x + 1) / 2 * width, (v0_NDC.y + 1) / 2 * height, 0); Vector3 v1_screen = new Vector3((v1_NDC.x + 1) / 2 * width, (v1_NDC.y + 1) / 2 * height, 0); Vector3 v2_screen = new Vector3((v2_NDC.x + 1) / 2 * width, (v2_NDC.y + 1) / 2 * height, 0); // Triagnle Bounding Box Vector2Int bboxMin = new Vector2Int((int)Mathf.Min(Mathf.Min(v0_screen.x, v1_screen.x), v2_screen.x), (int)Mathf.Min(Mathf.Min(v0_screen.y, v1_screen.y), v2_screen.y)); Vector2Int bboxMax = new Vector2Int((int)(Mathf.Max(Mathf.Max(v0_screen.x, v1_screen.x), v2_screen.x) + 0.5f), (int)(Mathf.Max(Mathf.Max(v0_screen.y, v1_screen.y), v2_screen.y) + 0.5f)); for (int i = bboxMin.x; i < bboxMax.x; i++) { for (int j = bboxMin.y; j < bboxMax.y; j++) { // Edge Function if (IsInsideTriangle(i + 0.5f, j + 0.5f, v0_screen, v1_screen, v2_screen)) { // 计算重心坐标 Vector3 barycentricCoordinate = BarycentricCoordinate(i + 0.5f, j + 0.5f, v0_screen, v1_screen, v2_screen); // 计算该像素在观察空间的深度值:观察空间中的z的倒数在屏幕空间是线性的,所以用重心坐标可以插值z的倒数,再进行转换求出该像素的观察空间中的深度 float z_view = 1.0f / (barycentricCoordinate.x / v0_view.z + barycentricCoordinate.y / v1_view.z + barycentricCoordinate.z / v2_view.z); // 插值投影后的z,首先投影后的z除以观察空间的z,用重心坐标插值后,再乘该像素观察空间的z float z_interpolated = z_view * (v0_NDC.z / v0_view.z * barycentricCoordinate.x + v1_NDC.z / v1_view.z * barycentricCoordinate.y + v2_NDC.z / v2_view.z * barycentricCoordinate.z); // Early-Z :) // 存储到Depth Buffer,从[-1,1]变换到[0,1] float z01 = (z_interpolated + 1) / 2f; if (z01 > m_FrameBuffer.GetDepth(i, j)) { continue; } else { m_FrameBuffer.SetDepth(i, j, z01); } // 插值顶点属性:颜色、uv、法线、副切线、世界坐标 Color color = z_view * (v0.color / v0_view.z * barycentricCoordinate.x + v1.color / v1_view.z * barycentricCoordinate.y + v2.color / v2_view.z * barycentricCoordinate.z); Vector2 uv = z_view * (v0.uv / v0_view.z * barycentricCoordinate.x + v1.uv / v1_view.z * barycentricCoordinate.y + v2.uv / v2_view.z * barycentricCoordinate.z); Vector3 normal = z_view * (v0.normal / v0_view.z * barycentricCoordinate.x + v1.normal / v1_view.z * barycentricCoordinate.y + v2.normal / v2_view.z * barycentricCoordinate.z); Vector4 tangent = z_view * (v0.tangent / v0_view.z * barycentricCoordinate.x + v1.tangent / v1_view.z * barycentricCoordinate.y + v2.tangent / v2_view.z * barycentricCoordinate.z); Vector3 worldPos = z_view * (v0_world / v0_view.z * barycentricCoordinate.x + v1_world / v1_view.z * barycentricCoordinate.y + v2_world / v2_view.z * barycentricCoordinate.z); Color col = Color.black; if (m_CurShader != null) { m_CurShader.modelMatrix = m_Model; m_CurShader.vertexColor = color; m_CurShader.uv = uv; m_CurShader.normal = normal; m_CurShader.tangent = tangent; if (m_CurShader is BlinnPhongShader) { BlinnPhongShader blinnPhongShader = (BlinnPhongShader)m_CurShader; blinnPhongShader.worldPos = worldPos; foreach (var light in m_Lights) { blinnPhongShader.lightColor = light.color; blinnPhongShader.lightIntensity = light.intensity; blinnPhongShader.lightPos = light.transform.position; col += blinnPhongShader.FragmentShade(); } } } m_FrameBuffer.SetColor(i, j, col); } } } }