private void CreateShader()
 {
     m_BlinnPhongShader = new BlinnPhongShader();
 }
Example #2
0
    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);
                }
            }
        }
    }