Unity中Shader观察空间推导(在Shader中实现)

news2025/1/14 18:38:21

文章目录

  • 前言
  • 一、观察空间矩阵推导
    • 1、求观察空间基向量
    • 2、求观察空间的基向量在世界空间中的矩阵 的 逆矩阵
    • 2、求平移变换矩阵
    • 3、相乘得出 观察空间转化矩阵
    • 4、得到顶点的世界空间坐标,然后转化到观察空间
    • 5、把观察空间坐标转化为齐次裁剪坐标输出到屏幕
  • 二、最终效果
    • 1、这是我们用默认Shader,在该摄像机坐标下的游戏界面
    • 2、使用我们的Shader,并且给我们的ViewPos赋值为摄像机坐标
    • 3、最终代码


前言

在上篇文章中,我们是实现了Shader中的观察空间推导。

  • Unity中Shader观察空间推导

我们在这篇文章中,根据上篇文章的推导,在Shader中实现观察空间矩阵的推导。


一、观察空间矩阵推导

  • Pview = [Wview] * Pworld

  • Pview = [Vworld]-1 * Pworld

  • Pview = [Vworld]T * Pworld

  • 在属性面板定义测试使用到的 摄像机坐标 和 测试顶点坐标

_ViewPos(“View Pos”,vector) = (0,0,0,0)
_ViewTarget(“View Target”,vector) = (0,0,0,0)

1、求观察空间基向量

  • Z坐标轴基向量

float3 ViewZ = normalize(_ViewPos - _ViewTarget);

  • 假设Y坐标轴基向量为(0,1,0)

float3 ViewY = float3(0,1,0);

  • 求 X 坐标基向量

float3 ViewX = cross(ViewZ,ViewY);

  • 求 Y 坐标基向量

ViewY = cross(ViewX,ViewZ);

2、求观察空间的基向量在世界空间中的矩阵 的 逆矩阵

在这里插入图片描述

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
);

2、求平移变换矩阵

1 0 0 − T   x   0 1 0 − T   y   0 0 1 − T   z   0 0 0 1 \begin{matrix} 1&0&0&-T~x~\\ 0&1&0&-T~y~\\ 0&0&1&-T~z~\\ 0&0&0&1\\ \end{matrix} 100001000010T x T y T z 1

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

3、相乘得出 观察空间转化矩阵

float4x4 M_view = mul(M_viewTemp,M_viewTranslate);

4、得到顶点的世界空间坐标,然后转化到观察空间

float3 vertexWS = TransformObjectToWorld(v.vertexOS);
float3 vertexVS = mul(M_view,float4(vertexWS,1));

5、把观察空间坐标转化为齐次裁剪坐标输出到屏幕

o.vertexCS = TransformWViewToHClip(vertexVS);


二、最终效果

1、这是我们用默认Shader,在该摄像机坐标下的游戏界面

在这里插入图片描述

2、使用我们的Shader,并且给我们的ViewPos赋值为摄像机坐标

请添加图片描述

3、最终代码

//平移变换
//缩放变换
//旋转变换(四维)
Shader "MyShader/URP/P3_6_5"
{
    Properties
    {
        _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)
    }
    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;
            };

            struct Varying
            {
                float4 vertexCS : SV_POSITION;
            };

            CBUFFER_START(UnityPerMaterial)
            float4 _Translate;
            float4 _Scale;
            float4 _Rotation;
            float4 _ViewPos;
            float4 _ViewTarget;
            CBUFFER_END
            Varying vert (Attribute v)
            {
                Varying o;
                //平移变换
                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));
                
                o.vertexCS = TransformWViewToHClip(vertexVS);
                
                //o.vertexCS = TransformObjectToHClip(v.vertexOS.xyz);
                return o;
            }

            half4 frag (Varying i) : SV_Target
            {
                return 1;
            }
            ENDHLSL
        }
    }
}

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

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

相关文章

Python入门学习篇(五)——列表字典

1 列表 1.1 定义 ①有序可重复的元素集合 ②可以存放不同类型的数据 ③个人理解:类似于java中的数组1.2 相关方法 1.2.1 获取列表长度 a 语法 len(列表名)b 示例代码 list2 [1, 2, "hello", 4] print(len(list2))c 运行结果 1.2.2 获取列表值 a 语法 列表名…

STM32实现三个小灯亮

led.c #include"led.h"void Led_Init(void) {GPIO_InitTypeDef GPIO_VALUE; //???RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???GPIO_VALUE.GPIO_ModeGPIO_Mode_Out_PP;//???? ????GPIO_VALUE.GPIO_PinGPIO_Pin_1|GPIO_Pin_2|GPIO_P…

深度学习中的池化

1 深度学习池化概述 1.1 什么是池化 池化层是卷积神经网络中常用的一个组件,池化层经常用在卷积层后边,通过池化来降低卷积层输出的特征向量,避免出现过拟合的情况。池化的基本思想就是对不同位置的特征进行聚合统计。池化层主要是模仿人的…

Openwrt AP 发射 WiFi 信号

问题 想一次把 OpenWrt 路由器 wifi 问题给解决,完全取代路由器。 使用 倍控的 N5105 设备,有 mPCIe 接口,使用了 intel AX200 无线网卡,支持 2.4G 与 5G。 设置步骤 OpenWrt 镜像 第一次使用的镜像不支持 wifi,在…

Docker知识总结

Docker 学习目标: 掌握Docker基础知识,能够理解Docker镜像与容器的概念 完成Docker安装与启动 掌握Docker镜像与容器相关命令 掌握Tomcat Nginx 等软件的常用应用的安装 掌握docker迁移与备份相关命令 能够运用Dockerfile编写创建容器的脚本 能够…

51单片机拆字程序实验

一、实验内容 1.基本要求 熟悉51仿真系统;设计并单步调试,实现将R5中数值(初值为本人学号后两位)拆分成两位独立的数据分别存于R6,R7中; 2.扩展要求 将R6,R7中的被拆出来的一位HEX数据转换为可显示的ASCII编码&…

Python算法例27 对称数

1. 问题描述 对称数是一个旋转180后(倒过来)看起来与原数相同的数,找到所有长度为n的对称数。 2. 问题示例 给出n2,返回["11","69","88&#x…

docker构建镜像及项目部署

文章目录 练习资料下载一、docker基础1. 基本概念2. docker常见命令3. 命令别名4. 数据卷 二、docker自定义镜像1. 了解镜像结构2. 了解Dockerfile3. 构建Dockerfile文件,完成自定义镜像 三、网络1. docker常见网络命令2. docker自带虚拟网络3. 自定义网络 四、dock…

Web前端 ---- 【Vue】vue路由守卫(全局前置路由守卫、全局后置路由守卫、局部路由path守卫、局部路由component守卫)

目录 前言 全局前置路由守卫 全局后置路由守卫 局部路由守卫之path守卫 局部路由守卫之component守卫 前言 本文介绍Vue2最后的知识点,关于vue的路由守卫。也就是鉴权,不是所有的组件任何人都可以访问到的,需要权限,而根据权限…

simulinkveristandlabview联合仿真——模型导入搭建人机界面

目录 1.软件版本 2.搭建simulink仿真模型 编译错误 3.导入veristand并建立工程 4.veristand导入labview labview显示veristand工程数据 labview设置veristand工程数据 运行labview工程 1.软件版本 matlab2020a,veristand2020 R4,labview2020 SP…

第1课 配置FFmpeg+OpenCV开发环境

一、配置开发环境 1.下载FFmpegOpenCV开发所用的SDK压缩包,并解压到E:\SDK下,解压后的路径应为:E:\SDK\ffmpeg-sdk\58\x86\dll及E:\SDK\opencv-sdk\340\x86\dll。 2.新建VC项目,名称为demo1,项目类弄为MFC应用程序&a…

2023年浙大城市学院新生程序设计竞赛(同步赛)G

登录—专业IT笔试面试备考平台_牛客网 题意 思路 首先想法非常单一,一定是去枚举操作点,然后看它染白和不染的价值差值 也就是说,把一个黑色结点染白之后,对哪些结点的价值会影响 不难想象其实就是操作结点的子树和该点连通的…

嵌入式硬件电路原理图之跟随电路

描述 电压跟随电路 电压跟随器是共集电极电路,信号从基极输入,射极输出,故又称射极输出器。基极电压与集电极电压相位相同,即输入电压与输出电压同相。这一电路的主要特点是:高输入电阻、低输出电阻、电压增益近似为…

C/C++ 连接访问 MySQL数据库

前面我们已经讲述了MySQL的基础使用,现在我们来看一下如何使用语言来操作数据库。在实际开发中,语言连接MySQL是为了能够在编程语言中与MySQL数据库进行交互和操作。大部分情况我们都是通过语言连接MySQL,建立与MySQL数据库的连接&#xff0c…

docker笔记2-docker 容器

docker 容器的运行 docker run 镜像名:版本标签: 创建 启动容器 docker run 镜像名 ,如果镜像不存在,则会在线下载镜像。 注意事项: 容器内的进程必须处于前台运行状态,不能后台(守护进程运行…

Android UID相关知识

一、UID/PID/GID/GIDS的含义和作用 UID : android中uid用于标识一个应用程序,uid在应用安装时被分配,并且在应用存在于手机上期间,都不会改变。一个应用程序只能有一个uid,多个应用可以使用sharedUserId 方式共享同一个uid&#…

【四】【C语言\动态规划】地下城游戏、按摩师、打家劫舍 II,三道题目深度解析

动态规划 动态规划就像是解决问题的一种策略,它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题,并将每个小问题的解保存起来。这样,当我们需要解决原始问题的时候,我们就可以直接利…

YOLOv8改进 | 主干篇 | 利用SENetV1改进网络结构 (ILSVRC冠军得主)

一、本文介绍 本文给大家带来的改进机制是SENet(Squeeze-and-Excitation Networks)其是一种通过调整卷积网络中的通道关系来提升性能的网络结构。SENet并不是一个独立的网络模型,而是一个可以和现有的任何一个模型相结合的模块(可以看作是一…

Linux 宝塔mysql莫名其妙数据库不见了恢复数据库

起因:宝塔安装的mysql 线上运行突然表包括库都不见了,想办法恢复数据库 登陆mysql cd /www/server/mysql/binmysql -u root -p查看binlog日志是否打开 show variables like log_%;log_bin如果为 ON 则为开启状态,如果开启了才可以进行下一…

研究论文 2022-Oncoimmunology:AI+癌RNA-seq数据 识别细胞景观

Wang, Xin, et al. "Deep learning using bulk RNA-seq data expands cell landscape identification in tumor microenvironment." Oncoimmunology 11.1 (2022): 2043662. https://www.tandfonline.com/doi/full/10.1080/2162402X.2022.2043662 被引次数&#xff1…