Unity里面CG和HLSL在写法上的一些区别

news2024/11/26 20:45:04

大家好,我是阿赵。这里继续讲URP相关的内容。
这次想讲的是CG和HLSL在写法上的一些区别。

一、为什么开始用HLSL

首先,基本上大家都知道的事情再说一遍。
三种Shader编程语言:
1、基于OpenGL的OpenGL Shading Language,缩写GLSL
2、基于DirectX的High Level Shading Language,缩写HLSL
3、基于NVIDIA的C for Graphic,缩写CG
简单来说GLSL和HLSL由于是分别基于不同的接口,所以两者是不能混用的,但CG却是可以同时被两种接口支持。
所以在早期的Unity版本里,最常见的是用CG Program来写Shader。但随着后来各种新技术的出现,CG已经有点跟不上步伐了,所以新版本的Unity里面的支持库渐渐变成了HLSL了。
比如我们之前在导入URP工程时,看到的支持库,全部都是HLSL的。基于这种情况下,我们也应该开始熟悉一下HLSL。

二、从CG转变到HLSL

由于语法是很类似的,所以我先写一个最基础的CG例子,然后再逐步转换成HLSL。

Shader "azhao/CGBase"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag


            //#include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
				//自己算矩阵转换,把顶点从模型空间转换到裁剪空间,并计算UV坐标
				float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
				float4 viewPos = mul(UNITY_MATRIX_V, worldPos);
				float4 clipPos = mul(UNITY_MATRIX_P, viewPos);
				o.pos = clipPos;
				o.uv = v.uv*_MainTex_ST.xy + _MainTex_ST.zw;
				//上面的计算在导入UnityCG.cginc后可以简化为
				//顶点坐标
				//o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				//或者
                //o.pos = UnityObjectToClipPos(v.vertex);
				//UV坐标
                //o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                half4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

这个例子非常简单,标准的顶点片段程序,值得注意的地方有
1、Unity自带的矩阵,比如unity_ObjectToWorld或者UNITY_MATRIX_MVP之类的,不需要声明,直接就可以使用
2、贴图是用sampler2D 类型来定义的
3、假如引用UnityCG.cginc,那么将会可以使用库里面内置的方法,比如UnityObjectToClipPos或者TRANSFORM_TEX等。
4、我特意不引用UnityCG.cginc,是为了在接下来的转换中,不依赖CG库提供的方法来做对比。

接下来,比较不规范的转换一版HLSL

Shader "azhao/HLSLBase"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
				float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;                
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			float4x4 unity_ObjectToWorld;
			float4x4 unity_MatrixVP;

            v2f vert (appdata v)
            {
				v2f o;
				float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
				float4 clipPos = mul(unity_MatrixVP, worldPos);
				o.pos = clipPos;
				o.uv = v.uv*_MainTex_ST.xy + _MainTex_ST.zw;
				return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                half4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDHLSL
        }
    }
}

首先说明的是,这个Shader虽然很多地方不规范,但在Unity里面是完全可以跑起来的。
然后看值得注意的地方:
1、程序体里面不再是使用CGPROGRAM和ENDCG来包裹程序内容了,而是改为了用HLSLPROGRAM和ENDHLSL
2、unity内置的矩阵,不能再直接使用,要先声明再使用了,比如unity_ObjectToWorld和unity_MatrixVP
3、在使用CG的时候,有些矩阵是等价的,比如unity_ObjectToWorld和UNITY_MATRIX_M是一样的,但在HLSL里面,如果没有引用核心库的情况下,拿UNITY_MATRIX_M、UNITY_MATRIX_V、UNITY_MATRIX_P这些矩阵直接计算,不报错,但计算不出正确结果。但类似unity_ObjectToWorld和unity_MatrixVP这类的矩阵是正确的。

下面再来看一个比较正确的版本:

Shader "azhao/HLSLBase2"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"}
        LOD 100

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

			#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
				float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;                
            };
			CBUFFER_START(UnityPerMaterial)
            float4 _MainTex_ST;
			CBUFFER_END
			TEXTURE2D(_MainTex);
			SAMPLER(sampler_MainTex); 
            v2f vert (appdata v)
            {
				v2f o;

				VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
				o.pos = vertexInput.positionCS;
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                half4 col = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex, i.uv);
                return col;
            }
            ENDHLSL
        }
    }
}

直接看值得注意的地方:
1、渲染管线可以指定一下"RenderPipeline" = “UniversalPipeline”
2、引入了一个HLSL的核心库Core.hlsl。这个东西是类似于UnityCG.cginc的库,里面带有非常多好用的方法,所以基本上来说,都需要引入的
3、在引入了Core.hlsl之后,那些内置矩阵不需要声明就可以使用了,而且unity_ObjectToWorld和UNITY_MATRIX_M又变成相同的了。这是因为,在核心库里面,对这些矩阵又重新做了一次定义
在这里插入图片描述

4、提供了直接转换顶点坐标到裁剪空间的方法GetVertexPositionInputs,这个方法返回的是一个VertexPositionInputs结构体,里面除了有裁剪空间的坐标,还有世界空间坐标和观察空间坐标。甚至还有ndc坐标。

VertexPositionInputs GetVertexPositionInputs(float3 positionOS)
{
    VertexPositionInputs input;
    input.positionWS = TransformObjectToWorld(positionOS);
    input.positionVS = TransformWorldToView(input.positionWS);
    input.positionCS = TransformWorldToHClip(input.positionWS);

    float4 ndc = input.positionCS * 0.5f;
    input.positionNDC.xy = float2(ndc.x, ndc.y * _ProjectionParams.x) + ndc.w;
    input.positionNDC.zw = input.positionCS.zw;

    return input;
}

5、定义贴图的方式变了

TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex); 

6、采样贴图的方式变了

half4 col = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex, i.uv);

SAMPLE_TEXTURE2D方法传入3个参数。

7、在声明变量的时候,通过CBUFFER_START和CBUFFER_END把在Properties里面有声明的变量包裹住,这样的做法是为了SRP Batcher的。需要注意的是,没有在Properties声明的变量,一般就是global全局变量,全局变量是不能包含在CBUFFER_START和CBUFFER_END里面的

8、fixed类型不再被支持,如果使用,会报错unrecognized identifier ‘fixed’

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

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

相关文章

接口测试中postman环境和用例集

postman的环境使用 postman里有环境的设置,就是我们常说的用变量代替一个固定的值,这样做的好处是可以切换不同的域名、不同的环境变量,不同的线上线下账户等等场景。下面就看下怎么用吧。 创建一个Environment postman有一个envrionment&am…

Java是如何实现双亲委托机制的

Java 是一种面向对象的编程语言,它有一套独特的类加载机制。其中,双亲委托加载机制是 Java 类加载机制中的一个重要概念。本文将介绍 Java 的双亲委托加载机制是如何实现的,并解释其作用和优点。 Java 类加载机制 在 Java 中,类的…

瀑布流组件陷入商品重复怪圈?我是如何用心一解的!

目录 背景 瀑布流组件 什么是瀑布流组件 如何实现一个瀑布流组件 商品重复的原因 解决方案 方法一(复杂,不推荐):标记位大法 方法二(优雅,推荐):Promise 队列 大法 总结 背…

解决新思路#报错:ping: www.baidu.com: 未知的名称或服务--照着步骤来还是ping不通的可能原因

最近由ubantu转为centos7,配置hadoop,配置静态ip的过程中一直ping不通。之前配置ubantu的也是,终于这次在重启虚拟机和主机后发现了原因。 中途尝试过: 1.三次以上命令行反复操作 2.图形界面设置 3.看是否主机的网络适配器的网关与设置的IP地址产生冲突…

JavaScript实现计算100之间能被5整除的数的代码

以下为实现计算100之间能被5整除的数的程序代码和运行截图 目录 前言 一、计算100之间能被5整除的数 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择,您可以在目录里进行快速查找; 2.本博文代码可以根据题…

2023最新100道渗透测试面试题(附答案)

眨眼间2023年快过去一半了,不知道大家有没有找到心仪的工作呀,今天我给大家整理了100道渗透测试面试题给大家,需要答案的话可以在评论区给我留言哦~ 第一套渗透面试题 什么是渗透测试?它的目的是什么? 渗透测试的五个…

DirectX12 简单入门(一)

在很久以前写过关于DirectX9的一些应用,直到现在DirectX12已经普及了。写完几个DirectX12测试代码之后,写一篇DirectX12简单入门介绍一下基本概念,以及环境搭建和编程过程。 编程环境 与DirectX9不同,在DirectX12开发中微软将需…

『MySQL 实战 45 讲』“order by” 是怎么工作的

“order by” 是怎么工作的 首先创建一个表 CREATE TABLE t ( id int(11) NOT NULL, city varchar(16) NOT NULL, name varchar(16) NOT NULL, age int(11) NOT NULL, addr varchar(128) DEFAULT NULL, PRIMARY KEY (id), KEY city (city) ) ENGINEInnoDB;全字段排序 在 cit…

自己搭建go web 框架

思想base部分day1:封装gee封装context上下文封装tree路由树分组封装group与中间件封装文件解析封装封装错误处理 思想 web框架服务主要围绕着请求与响应来展开的 搭建一个web框架的核心思想 1 便捷添加响应路径与响应函数(base) 2 能够接收多种数据类型传入(上下文context) 3 构…

【Linux】Linux入门学习之常用命令五

介绍 这里是小编成长之路的历程,也是小编的学习之路。希望和各位大佬们一起成长! 以下为小编最喜欢的两句话: 要有最朴素的生活和最遥远的梦想,即使明天天寒地冻,山高水远,路远马亡。 一个人为什么要努力&a…

支付系统设计五:对账系统设计01-总览

文章目录 前言一、对账系统构建二、执行流程三、获取支付渠道数据1.接口形式1.1 后台配置1.2 脚本编写1.2.1 模板1.2.2 解析脚本 2.FTP形式2.1 后台配置2.2 脚本编写2.2.1 模板2.2.2 解析脚本 四、获取支付平台数据五、数据比对1. 比对模型2. 比对器 总结 前言 从《支付系统设…

AE基础教程

一:粒子插件。 AEPR插件-Trapcode Suite V18.1.0 中文版 二:跟随手指特效。 1:空对象位置关键帧跟随手指。 2:发射粒子位置,按住Alt键,连接到空对象位置处。。 三:CtrI导入素材快捷键。 四&a…

Elasticsearch基础学习-常用查询和基本的JavaAPI操作ES

关于ES数据库的和核心倒排索引的介绍 一、Elasticsearch概述简介关于全文检索引擎关系型数据库的全文检索功能缺点全文检索的应用场景Elasticsearch 应用案例 二、Elasticsearch学习准备安装下载关于es检索的核心-倒排索引正向索引(forward index)倒排索…

辅助驾驶功能开发-功能规范篇(16)-2-领航辅助系统NAP-自动变道-1

书接上回 2.3.4.自动变道 当车辆处于导航引导模式NOA功能时(即车辆横向控制功能激活),且车速大于40km/h,驾驶员按下转向灯拨杆或系统判断当前有变道需要时,自动变道系统通过对车道线、自车道前方目标距离、邻近车道前后方目标距离等环境条件进行判断,在转向灯亮起3s后控…

看到这个数据库设计,我终于明白了我和其他软测人的差距

测试人员为什么要懂数据库设计? 更精准的掌握业务,针对接口测试、Web 测试,都是依照项目/产品需求进行用例设计,如果掌握数据库设计知识,能直接面对开发的数据表,更好、更精准的理解业务逻辑;有…

【滑动窗口】滑窗模板,在小小的算法题里滑呀滑呀滑

首先大家先喊出我们的口号:跟着模板搞,滑窗没烦恼! 一.什么是滑动窗口? 滑动窗口算法是双指针算法的一种特定化的算法模型,常用于在特定的条件下求最大或者最小的字符串,特定的数组,以及字符序列…

JAVA 可用的高性能docker镜像及如何使用?

目前docker hub上下载量很大的java、openjdk镜像都已经被弃用,不再维护,目前可用的java docker镜像有哪一些呢?哪一些镜像是主流的? 本文带有领略java可用的镜像资源、如何使用它们,如何构建springboot镜像? 1. 可用的java镜像 1.1. amazoncorretto 1.1.1. 什么是Corr…

环路详解:交换机环路产生的过程和原因图解

前言: 在了解环路之前得先了解交换机的工作原理,当然交换机的基本工作原理其实非常简单,只有“单播转发与泛洪转发”、“交换机MAC地址表”这两个!其他的如vlan,生成树等也是在此基础上增加的,弥补交换机基…

node笔记_koa框架的路由

文章目录 ⭐前言⭐koa 原生路由写法⭐引入 koa-router💖 安装koa-router💖 动态读取路径文件作为路由 ⭐结束 ⭐前言 大家好,我是yma16,本文介绍koa框架的路由。 往期文章 node_windows环境变量配置 node_npm发布包 linux_配置no…

[网络安全]DVWA之XSS(Reflected)攻击姿势及解题详析合集

[网络安全]DVWA之XSS(Reflected)攻击姿势及解题详析合集 XSS(Reflected)-low level源代码姿势 XSS(Reflected)-medium level源代码姿势1.双写绕过2.大小写绕过 XSS(Reflected)-high level源代码str_replace函数 姿势 XSS(Reflected)-Impossible level源代…