Unity中Shader裁剪空间推导(在Shader中实现)

发布时间:2023年12月27日


前言

我们在上一篇文章中,进行了正交相机视图空间下转化到裁剪空间下的矩阵推导。

我们在这篇文章中,在Unity的Shader中实现一下。


一、在Shader中,手动把正交相机的坐标转化到裁剪空间

  • OpenGL下:

[ 2 w 0 0 0 0 2 h 0 0 0 0 2 n ? f n + f n ? f 0 0 0 1 ] \begin{bmatrix} \frac{2}{w} & 0 & 0 & 0 \\ 0 & \frac{2}{h} & 0 &0\\ 0 & 0 & \frac{2}{n -f} &\frac{n + f}{n - f}\\ 0 & 0 & 0 & 1\\ \end{bmatrix} ?w2?000?0h2?00?00n?f2?0?00n?fn+f?1? ?

  • DirectX下:

[ 2 w 0 0 0 0 2 h 0 0 0 0 1 n ? f n n ? f 0 0 0 1 ] \begin{bmatrix} \frac{2}{w} & 0 & 0 & 0 \\ 0 & \frac{2}{h} & 0 &0\\ 0 & 0 & \frac{1}{n -f} &\frac{n}{n - f}\\ 0 & 0 & 0 & 1\\ \end{bmatrix} ?w2?000?0h2?00?00n?f1?0?00n?fn?1? ?

1、我们在属性面板定义一个变量,用于传入摄像机的信息

这里信息包括:
摄像机的Size(X)、近裁剪面(Y)、远裁剪面(Z) 和 屏幕宽高比(W)
1.7777……是 1920 / 1080 的比值。

_CameraParams(“Size(X),Near(Y),Far(Z) Ratio(W)”,Vector) = (0,0,0,1.777)

2、获取h、r、w、n、f

float h = _CameraParams.x * 2;
float w = h * _CameraParams.w;
float n = _CameraParams.y;
float f = _CameraParams.z;

3、获取OpenGL下的转化矩阵

//OpenGL
float4x4 M_clip01 = float4x4
(
	2/w,0,0,0,
	0,2/h,0,0,
	0,0,2/(n - f),(n + f) / (n - f),
	0,0,0,1
);

4、 获取DirectX下的转化矩阵

//DirectX
float4x4 M_clip02 = float4x4
(
	2/w,0,0,0,
	0,2/h,0,0,
	0,0,1/(n-f),n/(n-f),
	0,0,0,1
);

5、手动将观察空间下的坐标转换到裁剪空间下

//手动将观察空间下的坐标转换到裁剪空间下
o.vertexCS = mul(M_clip01,float4(vertexVS,1));

6、这里为测试模型效果,进行了纹理采样(可选)

  • 属性面板接收纹理

_MainTex(“MainTex”,2D) = “white”{}

  • 定义纹理 和 采样器

TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);

  • 在片元着色器中,采样并且使用

float4 mainTex = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.uv);
return mainTex;


二、最终效果

1、OpenGL下:

  • Edit->ProjectSetting->Player 中修改项目渲染平台设置
    在这里插入图片描述

//手动将观察空间下的坐标转换到裁剪空间下
o.vertexCS = mul(M_clip01,float4(vertexVS,1));

  • 传入当前摄像机信息
    在这里插入图片描述

2、DirectX下(需要注意的是,在DX平台下屏幕的 y 坐标是相反的,需要在转化矩阵的 y 对应位置乘以 -1):

  • Edit->ProjectSetting->Player 中修改项目渲染平台设置
    在这里插入图片描述

//手动将观察空间下的坐标转换到裁剪空间下
o.vertexCS = mul(M_clip02,float4(vertexVS,1));

  • 传入当前摄像机信息
    在这里插入图片描述

3、最终代码

//平移变换
//缩放变换
//旋转变换(四维)
//视图空间矩阵
//正交相机视图空间 -> 裁剪空间
Shader "MyShader/URP/P3_7_3"
{
    Properties
    {
        [Header(MainTexx)]
        _MainTex("MainTex",2D) = "white"{}
        [Header(Transtion)]
        _Translate("Translate(XYZ)",Vector) = (0,0,0,0)
        _Scale("Scale(XYZ)",Vector)= (1,1,1,1)
        _Rotation("Rotation(XYZ)",Vector) = (0,0,0,0)
        [Header(View)]
        _ViewPos("View Pos",vector) = (0,0,0,0)
        _ViewTarget("View Target",vector) = (0,0,0,0)
        [Header(Camera)]
        _CameraParams("Size(X),Near(Y),Far(Z) Ratio(W)",Vector) = (0,0,0,1.777)
        
    }
    SubShader
    {
        Tags
        {
            "PenderPipeline"="UniversalPipeline"
            "RenderType"="Opaque"
            "Queue"="Geometry"
        }
        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attribute
            {
                float4 vertexOS : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct Varying
            {
                float4 vertexCS : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            CBUFFER_START(UnityPerMaterial)
            float4 _Translate;
            float4 _Scale;
            float4 _Rotation;
            float4 _ViewPos;
            float4 _ViewTarget;
            float4 _CameraParams;
            CBUFFER_END
            
            TEXTURE2D(_MainTex);
            SAMPLER(sampler_MainTex);
            Varying vert (Attribute v)
            {
                Varying o;
                o.uv = v.uv;
                
                //平移变换
                float4x4 M_Translate = float4x4
                    (
                    1,0,0,_Translate.x,
                    0,1,0,_Translate.y,
                    0,0,1,_Translate.z,
                    0,0,0,1
                    );
                v.vertexOS = mul(M_Translate,v.vertexOS);
                //缩放交换
                float4x4 M_Scale = float4x4
                    (
                    _Scale.x,0,0,0,
                    0,_Scale.y,0,0,
                    0,0,_Scale.z,0,
                    0,0,0,1
                    );
                v.vertexOS = mul(M_Scale,v.vertexOS);
                //旋转变换
                float4x4 M_rotateX = float4x4
                    (
                    1,0,0,0,
                    0,cos(_Rotation.x),sin(_Rotation.x),0,
                    0,-sin(_Rotation.x),cos(_Rotation.x),0,
                    0,0,0,1
                    );
                float4x4 M_rotateY = float4x4
                    (
                    cos(_Rotation.y),0,sin(_Rotation.y),0,
                    0,1,0,0,
                    -sin(_Rotation.y),0,cos(_Rotation.y),0,
                    0,0,0,1
                    );
                float4x4 M_rotateZ = float4x4
                    (
                        cos(_Rotation.z),sin(_Rotation.z),0,0,
                        -sin(_Rotation.z),cos(_Rotation.z),0,0,
                        0,0,1,0,
                        0,0,0,1
                    );
                v.vertexOS = mul(M_rotateX,v.vertexOS);
                v.vertexOS = mul(M_rotateY,v.vertexOS);
                v.vertexOS = mul(M_rotateZ,v.vertexOS);

                //观察空间矩阵推导
                //P_view = [W_view] * P_world
                //P_view = [V_world]^-1 * P_world
                //P_view = [V_world]^T * P_world
                float3 ViewZ = normalize(_ViewPos - _ViewTarget);
                float3 ViewY = float3(0,1,0);
                float3 ViewX = cross(ViewZ,ViewY);
                ViewY = cross(ViewX,ViewZ);

                float4x4 M_viewTemp = float4x4
                    (
                        ViewX.x,ViewX.y,ViewX.z,0,
                        ViewY.x,ViewY.y,ViewY.z,0,
                        ViewZ.x,ViewZ.y,ViewZ.z,0,
                        0,0,0,1
                    );

                float4x4 M_viewTranslate = float4x4
                    (
                        1,0,0,-_ViewPos.x,
                        0,1,0,-_ViewPos.y,
                        0,0,1,-_ViewPos.z,
                        0,0,0,1
                    );

                float4x4 M_view = mul(M_viewTemp,M_viewTranslate);

                float3 vertexWS = TransformObjectToWorld(v.vertexOS);
                //世界空间转化到观察空间
                float3 vertexVS = mul(M_view,float4(vertexWS,1));

                //相机参数
                float h = _CameraParams.x * 2;
                float w = h * _CameraParams.w;
                float n = _CameraParams.y;
                float f = _CameraParams.z;
                //正交相机投影矩阵
                //P_Clip = [M_Clip] * P_view
                //OpenGL
                float4x4 M_clip01 = float4x4
                (
                    2/w,0,0,0,
                    0,2/h,0,0,
                    0,0,2/(n - f),(n + f) / (n - f),
                    0,0,0,1
                );
                //DirectX
                float4x4 M_clip02 = float4x4
                (
                    2/w,0,0,0,
                    0,2/h,0,0,
                    0,0,1/(n-f),n/(n-f),
                    0,0,0,1
                );
                //手动将观察空间下的坐标转换到裁剪空间下
                o.vertexCS = mul(M_clip02,float4(vertexVS,1));
                
                //观察空间 转化到 齐次裁剪空间
                //o.vertexCS = TransformWViewToHClip(vertexVS);
                
                //o.vertexCS = TransformObjectToHClip(v.vertexOS.xyz);
                return o;
            }

            half4 frag (Varying i) : SV_Target
            {
                float4 mainTex = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.uv);
                return mainTex;
            }
            ENDHLSL
        }
    }
}

文章来源:https://blog.csdn.net/qq_51603875/article/details/135230724
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。