Unity中Shader裁剪空间推导(在Shader中使用)

文章目录

  • 前言
  • 一、在Shader中使用转化矩阵
    • 1、在顶点着色器中定义转化矩阵
    • 2、用 UNITY_NEAR_CLIP_VALUE 区分平台矩阵
    • 3、定义一个枚举用于区分当前是处于什么相机
  • 二、我们在DirectX平台下,看看效果
    • 1、正交相机下
    • 2、透视相机下
    • 3、最终代码


前言

在上一篇文章中,我们推导得出了 透视相机到裁剪空间的转化矩阵

  • Unity中Shader裁剪空间推导(透视相机到裁剪空间的转化矩阵)

我们在正交矩阵Shader的基础上,继续测试

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

在这篇文章中,我们在Shader中使用该矩阵测试一下。

  • OpenGL
    [ 2 v w 0 0 0 0 2 n h 0 0 0 0 n + f n − f 2 n f n − f 0 0 − 1 0 ] \begin{bmatrix} \frac{2v}{w} & 0 & 0 & 0 \\ 0 & \frac{2n}{h} & 0 &0\\ 0 & 0 & \frac{n+f}{n-f} &\frac{2nf}{n-f}\\ 0 & 0 & -1 & 0\\ \end{bmatrix} w2v0000h2n0000nfn+f100nf2nf0
  • DirectX
    [ 2 v w 0 0 0 0 2 n h 0 0 0 0 n f − n n f f − n 0 0 − 1 0 ] \begin{bmatrix} \frac{2v}{w} & 0 & 0 & 0 \\ 0 & \frac{2n}{h} & 0 &0\\ 0 & 0 & \frac{n}{f-n} &\frac{nf}{f-n}\\ 0 & 0 & -1 & 0\\ \end{bmatrix} w2v0000h2n0000fnn100fnnf0

一、在Shader中使用转化矩阵

1、在顶点着色器中定义转化矩阵

  • OpenGL:

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

  • DirectX:

M_clipP = float4x4
(
2n/w,0,0,0,
0,2
n/h,0,0,
0,0,n/(f-n),(n*f)/(f-n),
0,0,-1,0
);

2、用 UNITY_NEAR_CLIP_VALUE 区分平台矩阵

  • 1为OpenGL
  • -1为DirectX

3、定义一个枚举用于区分当前是处于什么相机

[Enum(OrthoGraphic,0,Perspective,1)]_CameraType(“CameraType”,Float) = 0

  • 在手动转化矩阵时使用三目运算符来决定使用哪一个矩阵

float4x4 M_clip = _CameraType ? M_clipP : M_clipO;
o.vertexCS = mul(M_clip,float4(vertexVS,1));


二、我们在DirectX平台下,看看效果

1、正交相机下

在这里插入图片描述

2、透视相机下

在这里插入图片描述

3、最终代码

//平移变换
//缩放变换
//旋转变换(四维)
//视图空间矩阵
//正交相机视图空间 -> 裁剪空间
Shader "MyShader/URP/P3_7_6"
{
    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)
        [Enum(OrthoGraphic,0,Perspective,1)]_CameraType("CameraType",Float) = 0
        
    }
    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;
            float _CameraType;
            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.xyz - _ViewTarget.xyz);
                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.xyz);
                //世界空间转化到观察空间
                float3 vertexVS = mul(M_view,float4(vertexWS,1)).xyz;

                //相机参数
                float h = _CameraParams.x * 2;
                float w = h * _CameraParams.w;
                float n = _CameraParams.y;
                float f = _CameraParams.z;
                //正交相机投影矩阵
                //P_Clip = [M_Clip] * P_view
                float4x4 M_clipO;
                if(UNITY_NEAR_CLIP_VALUE==-1)
                {
                    //OpenGL
                    M_clipO = float4x4
                    (
                        2/w,0,0,0,
                        0,2/h,0,0,
                        0,0,2/(n - f),(n + f) / (n - f),
                        0,0,0,1
                    );
                }
                if(UNITY_NEAR_CLIP_VALUE==1)
                {
                    //DirectX
                    M_clipO = float4x4
                    (
                        2/w,0,0,0,
                        0,2/h,0,0,
                        0,0,1/(f-n),f/(f-n),
                        0,0,0,1
                    );
                }
                //透视相机投影矩阵
                float4x4 M_clipP;
                if(UNITY_NEAR_CLIP_VALUE==-1)
                {
                    //OpenGL
                    M_clipP = float4x4
                    (
                        2*n/w,0,0,0,
                        0,2*n/h,0,0,
                        0,0,(n+f)/(n-f),(2*n*f)/(n-f),
                        0,0,-1,0
                    );
                }
                if(UNITY_NEAR_CLIP_VALUE==1)
                {
                    //DirectX
                    M_clipP = float4x4
                    (
                        2*n/w,0,0,0,
                        0,2*n/h,0,0,
                        0,0,n/(f-n),(n*f)/(f-n),
                        0,0,-1,0
                    );
                }
                
                //手动将观察空间下的坐标转换到裁剪空间下
                float4x4 M_clip = _CameraType ? M_clipP : M_clipO;
                o.vertexCS = mul(M_clip,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
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/277857.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux驱动开发学习笔记6《蜂鸣器实验》

目录 一、蜂鸣器驱动原理 二、硬件原理分析 三、实验程序编写 1、 修改设备树文件 (1)添加pinctrl节点 (2)添加BEEP设备节点 (3)检查PIN 是否被其他外设使用 2、蜂鸣器驱动程序编写 3、编写测试AP…

【JS逆向学习】快乐学堂

逆向目标 登陆接口:https://www.91118.com/passport/Account/LoginPost?r0.20790763112591337&kdsyes&username13127519353&passbb3mlkFBqqo%3D&recordPwd1&ckcode5719&fscodeklxt&invite 加密参数: r:0.2079…

机器学习模型可解释性的结果分析

模型的可解释性是机器学习领域的一个重要分支,随着 AI 应用范围的不断扩大,人们越来越不满足于模型的黑盒特性,与此同时,金融、自动驾驶等领域的法律法规也对模型的可解释性提出了更高的要求,在可解释 AI 一文中我们已…

Linux开发工具——gdb篇

Linux下调试工具——gdb 文章目录 makefile自动化构建工具 gdb背景 gdb的使用 常用命令 总结 前言: 编写代码我们使用vim,编译代码我们使用gcc/g,但是我们,不能保证代码没问题,所以调试是必不可少的。与gcc/vim一样&…

Python中使用SQLite数据库的方法2-2

3.3.2 创建表单及字段 通过“3.2 创建Cursor类的对象”中创建的Cursor类的对象cur创建表单及字段,代码如图5所示。 图5 创建表单及字段 从图5中可以看出,通过Cursor类的对象cur调用了Cursor类的execute()方法来执行SQL语句。该方法的参数即为要指定的S…

在电商行业中,如何采集电商数据使用数据分析提高业务绩效

数据分析丨知识点丨电商数据采集 福利指路:文章底部领取《数据分析全家桶》 随着电子商务的不断发展,越来越多的企业开始使用数据分析来提高业务绩效。数据分析可以帮助电商企业更好地理解市场和客户,以制定更有针对性的营销策略和产品方案。…

ksuser.dll文件缺失怎么办?软件或游戏无法启动,一键自动修复

很多小伙伴反馈,自己的电脑中了病毒,被杀毒软件清理后,在打开游戏或软件的时候,经常会报错“提示无法找到ksuser.dll文件,建议重新安装软件或游戏”。自己根据提示重装后,还是报错,不知道应该怎…

两向量叉乘值为对应平行四边形面积--公式推导

两向量叉乘值为对应平行四边形面积--公式推导 介绍 介绍

[电磁学]大学物理陈秉乾老师课程笔记

主页有博主其他上万字的精品笔记,都在不断完善ing~ 第一讲 绪论,库仑定律 主要讲解了电磁学中的库伦定律和电场的相关概念,介绍了电荷和电磁相互作用的规律,并讲解了电场强度和电势的概念。 03:14 🎓 库伦定律:电势能与电荷的关…

【JAVA核心知识】分布式事务框架Seata

Seata 基本信息 GitHub:https://github.com/seata/seatastars: 20.6k 最新版本: v1.6.1 Dec 22, 2022 官方文档:http://seata.io/zh-cn/index.html 注意 官方仅仅支持同步调用。 官方在FAQ中表示对于异步框架需要自行支持。 具体的扩展思…

【Maven】<scope>provided</scope>

在Maven中,“provided”是一个常用的依赖范围,它表示某个依赖项在编译和测试阶段是必需的,但在运行时则由外部环境提供,不需要包含在最终的项目包中。下面是对Maven scope “provided”的详细解释: 编译和测试阶段可用…

关于2024年度PMI认证考试计划的通知

尊敬的考生: 经PMI和中国国际人才交流基金会研究决定,2024年度中国大陆地区计划举办四次PMI认证考试,3月、6月、8月、11月各举办一次,具体考试日期另行公布。如遇特殊情况需变更考试计划的,将提前另行通知。 PMI&#…

Ubuntu安装K8S(1.28版本,基于containrd)

原文网址:Ubuntu安装K8S(1.28版本,基于containrd)-CSDN博客 简介 本文介绍Ubuntu安装K8S的方法。 官网文档:这里 1.安装K8S 1.让apt支持SSL传输 sudo apt-get update sudo apt-get -y install apt-transport-https ca-certi…

超声波清洗机实测!北柏、希亦、洁盟超声波清洗机哪款清洁强?

眼镜清洗其实是一件非常重要的事情,一定不要忽视,表面看眼镜已经清洗干净了,实际眼镜缝隙中的杂污渍还没清洁到位,时间一长就会非常容易滋生细菌以及长螨虫!为了杜绝这种情况发生,大家务必重视起清洗眼镜&a…

SpringBoot2.7.12整合Knife4j

SpringBoot2.7.12整合Knife4j 是什么 Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案 添加依赖 <!--引入Knife4j的官方start包,该指南选择Spring Boot版本<3.0,开发者需要注意--> <dependency><groupId>com.github.xiaoymin</groupId>&l…

Zookeeper-Zookeeper特性与节点数据类型详解

1.Zookeeper介绍 ZooKeeper 是一个开源的分布式协调框架&#xff0c;是Apache Hadoop 的一个子项目&#xff0c;主要用来解决分布式集群中应用系统的一致性问题。Zookeeper 的设计目标是将那些复杂目容易出错的分布式一致性服务封装起来&#xff0c;构成一高效可靠的原语集&…

如何本地搭建FastDFS文件服务器并实现远程访问【内网穿透】

文章目录 前言1. 本地搭建FastDFS文件系统1.1 环境安装1.2 安装libfastcommon1.3 安装FastDFS1.4 配置Tracker1.5 配置Storage1.6 测试上传下载1.7 与Nginx整合1.8 安装Nginx1.9 配置Nginx 2. 局域网测试访问FastDFS3. 安装cpolar内网穿透4. 配置公网访问地址5. 固定公网地址5.…

交换两个数字的三种方法-LeetCode做题总结 344

344. 反转字符串 题解Java知识点交换两个数字的三种方法1、temp2、异或3、 题解 class Solution {public void reverseString(char[] s) {char temp;for(int i0,js.length-1; i<j; i,j--) {temp s[i];s[i] s[j];s[j] temp;}} }Java知识点 交换两个数字的三种方法 1、t…

Flutter BottomSheet 拖动分两段展示

第一段 第二段 实现思路 通过 GestureDetector 的 Drag 方法&#xff0c;动态改变Dialog的高度&#xff0c;通过设置一个最大高度和最小高度分成两层进行展示 实现 常用的展示BottomSheet的方法为 showModalBottomSheet /// 设置最高最好以高度的比例进行设置&#xff0c;方…

3、Git分支操作与团队协作

Git分支操作 1.什么是分支2. 分支的好处3. 分支的操作3.1 查看分支3.2 创建分支3.3 切换分支3.4 修改分支3.5 合并分支3.6 产生和解决冲突 4. 创建分支和切换分支图解5. Git团队协作机制团队内协作跨团队协作 均在git bash中进行操作。事先建好本地工作库 1.什么是分支 在版本…