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

news2024/11/17 19:50:44

文章目录

  • 前言
  • 一、在Shader中,手动把正交相机的坐标转化到裁剪空间
    • 1、我们在属性面板定义一个变量,用于传入摄像机的信息
    • 2、获取h、r、w、n、f
    • 3、获取OpenGL下的转化矩阵
    • 4、 获取DirectX下的转化矩阵
    • 5、手动将观察空间下的坐标转换到裁剪空间下
    • 6、这里为测试模型效果,进行了纹理采样(可选)
  • 二、最终效果
    • 1、OpenGL下:
    • 2、DirectX下(需要注意的是,在DX平台下屏幕的 y 坐标是相反的,需要在转化矩阵的 y 对应位置乘以 -1):
    • 3、可以使用 UNITY_NEAR_CLIP_VALUE 来判断是什么平台
    • 4、最终代码


前言

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

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

我们在这篇文章中,在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} w20000h20000nf2000nfn+f1

  • 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} w20000h20000nf1000nfn1

  • ReversedZ

在DirectX中,因为精度问题
会把原本的范围从[0,1]修改为[1,0]。所以,矩阵需要按照之前的方法重新推导

[ 2 w 0 0 0 0 2 h 0 0 0 0 1 f − n f f − n 0 0 0 1 ] \begin{bmatrix} \frac{2}{w} & 0 & 0 & 0 \\ 0 & \frac{2}{h} & 0 &0\\ 0 & 0 & \frac{1}{f-n} &\frac{f}{f-n}\\ 0 & 0 & 0 & 1\\ \end{bmatrix} w20000h20000fn1000fnf1

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/(f-n),f/(f-n),
	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、可以使用 UNITY_NEAR_CLIP_VALUE 来判断是什么平台

裁剪空间下的近剪裁值,(DX为1,OpenGL为-1).

4、最终代码

//平移变换
//缩放变换
//旋转变换(四维)
//视图空间矩阵
//正交相机视图空间 -> 裁剪空间
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
                float4x4 M_clip;
                if(UNITY_NEAR_CLIP_VALUE==-1)
                {
                    //OpenGL
                    M_clip = 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_clip = float4x4
                    (
                        2/w,0,0,0,
                        0,-2/h,0,0,
                        0,0,1/(f-n),f/(f-n),
                        0,0,0,1
                    );
                }
                
                
                //手动将观察空间下的坐标转换到裁剪空间下
                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
        }
    }
}


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 对象内存布局

在虚拟机中,Java对象在内存中的布局可以分为三块: 对象头(Header) :包含 markword 标记字段和类型指针,32 位上大小是 8 个字节,64 位 16 个字节,实例数据(Instance Dat…

sql_lab之sqli中的搜索型注入

搜索型注入 原理是运用模糊查询: select * from users where username like %a% 1.找到具有模糊查询的搜索框的注入点 2.构造闭合 因为模糊查询的代码是 select * from users where username like %a% 所以应该 鱼%’ -- s 判断构造闭合的函数是否正确 鱼%…

【DeepLearning】Deep Residual Learning for Image Recognition恺神大作学习

[TOC] Deep Residual Learning for Image Recognition 论文 1. 文章主要想解决什么问题,用了什么方法 深度神经网络在训练过程中的3个关键问题: 梯度消失/爆炸问题:随着网络层数的增加,梯度在反向传播过程中可能会变得非常小&a…

浅谈政企风险防控体系

政企风控体系是集团客户部在生产运营过程中对政企产品可能面临的风险进行监测,针对监测发现的风险,制定相应的管控措施,并跟进落实,以最大限度地减少或消除风险对组织的影响的管控体系。 本文之所以称为“浅谈”是因为文中主要围绕…

一条查询SQL是如何执行的?更新、新增、删除呢?

你好,我是田哥 本文共14629字,读完预计需要37分钟,建议先收藏。 大部分朋友估计都只知道写sql然后执行,但是并不知道MySQL背后到底是怎么实现的。 八股文中也有这么一道题:在MySQL中,一条SQL到底是如何执行…

【大数据存储与处理】开卷考试总复习笔记

文章目录 实验部分一、 HBase 的基本操作1. HBase Shell入门2. HBase创建数据库表3. HBase数据操作4. HBase删除数据库表5. HBase Python基本编程 before二、 HBase 过滤器操作1.创建表和插入数据2.行键过滤器3.列族与列过滤器4.值过滤器5.其他过滤器6.python hbase 过滤器编程…

通过 conda 安装 的 detectron2

从 detectron2官网 发现预编译的版本最高支持 pytorch1.10、cuda11.3。(2023-12-26) 1、安装 conda 环境。 conda create --name detectron2 python3.8 2、安装 pytorch1.10 和 cuda11.3。 pip3 install torch1.10.0cu113 torchvision0.11.1cu113 torc…

Hadoop之HDFS 详细教程

1、HDFS概述 Hadoop 分布式系统框架中,首要的基础功能就是文件系统,在 Hadoop 中使用FileSystem 这个抽象类来表示我们的文件系统,这个抽象类下面有很多子实现类,究竟使用哪一种,需要看我们具体的实现类,在…

CTFshow-pwn入门-栈溢出pwn41-pwn42

pwn41 我们首先将pwn文件下载下来,拖入到虚拟机查看一下文件的保护信息。 chmod x pwn checksec pwn该文件只开启了栈不可执行,并且文件是32位的。 我们把文件托到ida32中查看一下反编译代码。 int __cdecl main(int argc, const char **argv, const c…

大数据开发之Sqoop详细介绍

测试环境 CDH 6.3.1 Sqoop 1.4.7 一.Sqoop概述 Apache Sqoop(SQL-to-Hadoop)项目旨在协助RDBMS与Hadoop之间进行高效的大数据交流。用户可以在 Sqoop 的帮助下,轻松地把关系型数据库的数据导入到 Hadoop 与其相关的系统 (如HBase和Hive)中&…

呼叫中心知识库管理

呼叫中心向客户提供所需服务与支持的过程中,会遇到形形色色的客户和各式各样的问题,需要客服人员做出回答。客户对于客服人员的答复是否满意直接关系着呼叫中心的业绩,甚至企业的兴衰。而OKCC的知识库管理正是基于呼叫中心的这一需求而存在&a…

Python range函数新手指南:详细解析内部机制

更多资料获取 📚 个人网站:ipengtao.com range函数是Python中常用的内置函数之一,用于生成一系列连续的整数。本文将深入探讨range函数的内部实现,以揭示其工作原理和效率。 range函数的基本用法 range函数的基本用法。它通常使…

async和await的使用

async和await是promise的一种语法糖,也就是更简单易懂的写法。 在很多项目中,你会经常看到async和await的配合使用,看到原始的promise写法反而不多,就是因为async-await这种写法是用同步的语法去实现异步的逻辑。 基础使用 原生promise写法 let value nulllet proFn new P…

最新腾讯云2核2G轻量服务器性价比首选62元一年!

继昨天阿里云2核2G3M带宽轻量应用服务器降价到63元后,腾讯云迅速做出响应,腾讯云轻量2核2G3M服务器降价到62元一年,1元之差,你选择阿里云还是腾讯云?值得一提的是,阿里云不限制月流量,但是腾讯云…

【廖雪峰Java】Java基础知识

学习课程:廖雪峰的官方网站:https://www.liaoxuefeng.com/ 1、简介 1.1、Java之父 詹姆斯高斯林(James Gosling) 1.2、Java三个不同版本 Java SE:Standard EditionJava EE:Enterprise EditionJava ME&a…

openGauss学习笔记-174 openGauss 数据库运维-备份与恢复-导入数据-管理并发写入操作

文章目录 openGauss学习笔记-174 openGauss 数据库运维-备份与恢复-导入数据-管理并发写入操作174.1 事务隔离说明174.2 写入和读写操作174.3 并发写入事务的潜在死锁情况 openGauss学习笔记-174 openGauss 数据库运维-备份与恢复-导入数据-管理并发写入操作 174.1 事务隔离说…

生存分析序章2——生存分析之Python篇:lifelines库入门

目录 写在开头1. 介绍 lifelines 库1.1 lifelines库简介1.2 安装与环境配置 2. 数据准备2.1 数据格式与结构2.2 处理缺失数据2.3 对异常值的处理 3. Kaplan-Meier 曲线3.1 使用 lifelines 绘制生存曲线3.2 曲线解读3.3 额外补充 4. Cox 比例风险模型4.1 lifelines 中的 Cox 模型…

Echarts中饼图-实现放大显示数据

示例 代码演示 option {tooltip: {trigger: item},legend: {top: 5%,left: center},series: [{name: Access From,type: pie,radius: [40%, 70%],avoidLabelOverlap: false,label: {show: false,position: center},emphasis: {scale: true,//是否开启高亮后扇区的放大效果。s…

跟着LearnOpenGL学习12--光照贴图

文章目录 一、前言二、漫反射贴图三、镜面光贴图3.1、采样镜面光贴图 一、前言 在跟着LearnOpenGL学习11–材质中,我们讨论了让每个物体都拥有自己独特的材质从而对光照做出不同的反应的方法。这样子能够很容易在一个光照的场景中给每个物体一个独特的外观&#xf…

若依common模块解读,Java小白入门(四)

● 若依springboot版本后端,有两个核心模块,一个是ruoyi-common,一个是ruoyi-framewok,今天我们来看第一个模块ruoyi-common, 一些基类,工具等框架底座,分别来看其包含哪些基本内容: 1. annotation 注解,…