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

这篇具有很好参考价值的文章主要介绍了Unity里面CG和HLSL在写法上的一些区别。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

回到目录

大家好,我是阿赵。这里继续讲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又变成相同的了。这是因为,在核心库里面,对这些矩阵又重新做了一次定义
Unity里面CG和HLSL在写法上的一些区别,Unity引擎Shader效果,unity,游戏引擎,HLSL,URP

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’

9、坐标转换
TransformObjectToWorld
TransformWorldToView
TransformWorldToHClip
这些转换的方法,其实是包含在SpaceTransforms.hlsl里面。由于我们引入了Core.hlsl,而Core.hlsl里面引入了Input.hlsl,Input.hlsl里面引入了SpaceTransforms.hlsl,所以才能使用这些方法,把里面的内容列出来,方便查看文章来源地址https://www.toymoban.com/news/detail-550011.html

#ifndef UNITY_SPACE_TRANSFORMS_INCLUDED
#define UNITY_SPACE_TRANSFORMS_INCLUDED

// Return the PreTranslated ObjectToWorld Matrix (i.e matrix with _WorldSpaceCameraPos apply to it if we use camera relative rendering)
float4x4 GetObjectToWorldMatrix()
{
    return UNITY_MATRIX_M;
}

float4x4 GetWorldToObjectMatrix()
{
    return UNITY_MATRIX_I_M;
}

float4x4 GetWorldToViewMatrix()
{
    return UNITY_MATRIX_V;
}

// Transform to homogenous clip space
float4x4 GetWorldToHClipMatrix()
{
    return UNITY_MATRIX_VP;
}

// Transform to homogenous clip space
float4x4 GetViewToHClipMatrix()
{
    return UNITY_MATRIX_P;
}

// This function always return the absolute position in WS
float3 GetAbsolutePositionWS(float3 positionRWS)
{
#if (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0)
    positionRWS += _WorldSpaceCameraPos;
#endif
    return positionRWS;
}

// This function return the camera relative position in WS
float3 GetCameraRelativePositionWS(float3 positionWS)
{
#if (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0)
    positionWS -= _WorldSpaceCameraPos;
#endif
    return positionWS;
}

real GetOddNegativeScale()
{
    return unity_WorldTransformParams.w;
}

float3 TransformObjectToWorld(float3 positionOS)
{
    return mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0)).xyz;
}

float3 TransformWorldToObject(float3 positionWS)
{
    return mul(GetWorldToObjectMatrix(), float4(positionWS, 1.0)).xyz;
}

float3 TransformWorldToView(float3 positionWS)
{
    return mul(GetWorldToViewMatrix(), float4(positionWS, 1.0)).xyz;
}

// Transforms position from object space to homogenous space
float4 TransformObjectToHClip(float3 positionOS)
{
    // More efficient than computing M*VP matrix product
    return mul(GetWorldToHClipMatrix(), mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0)));
}

// Tranforms position from world space to homogenous space
float4 TransformWorldToHClip(float3 positionWS)
{
    return mul(GetWorldToHClipMatrix(), float4(positionWS, 1.0));
}

// Tranforms position from view space to homogenous space
float4 TransformWViewToHClip(float3 positionVS)
{
    return mul(GetViewToHClipMatrix(), float4(positionVS, 1.0));
}

// Normalize to support uniform scaling
float3 TransformObjectToWorldDir(float3 dirOS, bool doNormalize = true)
{
    float3 dirWS = mul((float3x3)GetObjectToWorldMatrix(), dirOS);
    if (doNormalize)
        return SafeNormalize(dirWS);

    return dirWS;
}

// Normalize to support uniform scaling
float3 TransformWorldToObjectDir(float3 dirWS, bool doNormalize = true)
{
    float3 dirOS = mul((float3x3)GetWorldToObjectMatrix(), dirWS);
    if (doNormalize)
        return normalize(dirOS);

    return dirOS;
}

// Tranforms vector from world space to view space
real3 TransformWorldToViewDir(real3 dirWS, bool doNormalize = false)
{
    float3 dirVS = mul((real3x3)GetWorldToViewMatrix(), dirWS).xyz;
    if (doNormalize)
        return normalize(dirVS);

    return dirVS; 
}

// Tranforms vector from world space to homogenous space
real3 TransformWorldToHClipDir(real3 directionWS, bool doNormalize = false)
{
    float3 dirHCS = mul((real3x3)GetWorldToHClipMatrix(), directionWS).xyz;
    if (doNormalize)
        return normalize(dirHCS);

    return dirHCS;
}

// Transforms normal from object to world space
float3 TransformObjectToWorldNormal(float3 normalOS, bool doNormalize = true)
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
    return TransformObjectToWorldDir(normalOS, doNormalize);
#else
    // Normal need to be multiply by inverse transpose
    float3 normalWS = mul(normalOS, (float3x3)GetWorldToObjectMatrix());
    if (doNormalize)
        return SafeNormalize(normalWS);

    return normalWS;
#endif
}

// Transforms normal from world to object space
float3 TransformWorldToObjectNormal(float3 normalWS, bool doNormalize = true)
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
    return TransformWorldToObjectDir(normalWS, doNormalize);
#else
    // Normal need to be multiply by inverse transpose
    float3 normalOS = mul(normalWS, (float3x3)GetObjectToWorldMatrix());
    if (doNormalize)
        return SafeNormalize(normalOS);

    return normalOS;
#endif
}

real3x3 CreateTangentToWorld(real3 normal, real3 tangent, real flipSign)
{
    // For odd-negative scale transforms we need to flip the sign
    real sgn = flipSign * GetOddNegativeScale();
    real3 bitangent = cross(normal, tangent) * sgn;

    return real3x3(tangent, bitangent, normal);
}

real3 TransformTangentToWorld(real3 dirTS, real3x3 tangentToWorld)
{
    // Note matrix is in row major convention with left multiplication as it is build on the fly
    return mul(dirTS, tangentToWorld);
}

// This function does the exact inverse of TransformTangentToWorld() and is
// also decribed within comments in mikktspace.h and it follows implicitly
// from the scalar triple product (google it).
real3 TransformWorldToTangent(real3 dirWS, real3x3 tangentToWorld)
{
    // Note matrix is in row major convention with left multiplication as it is build on the fly
    float3 row0 = tangentToWorld[0];
    float3 row1 = tangentToWorld[1];
    float3 row2 = tangentToWorld[2];
    
    // these are the columns of the inverse matrix but scaled by the determinant
    float3 col0 = cross(row1, row2);
    float3 col1 = cross(row2, row0);
    float3 col2 = cross(row0, row1);
    
    float determinant = dot(row0, col0);
    float sgn = determinant<0.0 ? (-1.0) : 1.0;
    
    // inverse transposed but scaled by determinant
    // Will remove transpose part by using matrix as the first arg in the mul() below
    // this makes it the exact inverse of what TransformTangentToWorld() does.
    real3x3 matTBN_I_T = real3x3(col0, col1, col2);
    
    return SafeNormalize( sgn * mul(matTBN_I_T, dirWS) );
}

real3 TransformTangentToObject(real3 dirTS, real3x3 tangentToWorld)
{
    // Note matrix is in row major convention with left multiplication as it is build on the fly
    real3 normalWS = TransformTangentToWorld(dirTS, tangentToWorld);
    return TransformWorldToObjectNormal(normalWS);
}

real3 TransformObjectToTangent(real3 dirOS, real3x3 tangentToWorld)
{
    // Note matrix is in row major convention with left multiplication as it is build on the fly

    // don't normalize, as normalWS will be normalized after TransformWorldToTangent 
    float3 normalWS = TransformObjectToWorldNormal(dirOS, false);
    
    // transform from world to tangent
    return TransformWorldToTangent(normalWS, tangentToWorld);
}

#endif

到了这里,关于Unity里面CG和HLSL在写法上的一些区别的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity和UE4的一些基础区别

    https://docs.unrealengine.com/5.0/zh-CN/essential-unreal-engine-material-concepts/(材质基本概念) Unity中通过使用shaderLab语言编写生成不同效果的材质球(现在URP和HDRP也有shadergraph---可视化编辑脚本) 而UE中通过蓝图用节点的方式封装,不需要直接使用HLSL语言编写生成材质 UE中材质蓝图是由

    2024年02月09日
    浏览(26)
  • Unity、UE、Cocos游戏开发引擎的区别

    Unity、Unreal Engine(UE)和Cocos引擎是三个常用的游戏开发引擎,它们在功能和特性上有一些区别。以下是它们之间的主要区别: 编程语言:Unity使用C#作为主要的编程语言,开发者可以使用C#脚本进行游戏逻辑编写。Unreal Engine主要使用C++作为编程语言,但也支持蓝图系统,允许

    2024年02月22日
    浏览(47)
  • 【unity】URP的shader开发中支持多光源,_ADDITIONAL_LIGHTS_VERTEX 和 _ADDITIONAL_LIGHTS 区别

    项目里有一个其他同事实现的shader,美术那边希望能支持多个光源, 我一看代码里面, frag 函数里已经实现了   代码也加了:             #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS 材质里加了这个keyword还是没起作用,   若宏控制注了有效。  一开始没搞明白

    2024年02月11日
    浏览(37)
  • 【.Net |minimal hosting model 】Program.cs 里面的不同写法

    本文介绍.net6.0中引入的minimal hosting model和如何将.net6.0以前的版本转换成6.0 在入门.net的过程中,我发现program.cs里面的写法有些是长这样的: 有些是长这样的 后者看起来更正式一点,所以我以为第一种是闹着玩的,真正的项目是不会这么用的,但是当我仔细看官方文档的时候

    2023年04月08日
    浏览(27)
  • UE(Unreal Engine,虚幻引擎)和Unity的区别与联系

    一、基本概念 游戏制作软件中最著名的两个游戏引擎是UE和Unity,二者有各自的特点和适用场景。 UE是指Unreal Engine(虚幻引擎)的简称,它是由Epic Games(Epic游戏公司)开发的一款高度先进的游戏开发工具。UE是一种3D图形渲染引擎,为开发者提供了一系列强大的功能,使他们

    2024年04月13日
    浏览(33)
  • UE虚幻引擎,Unity3D,Blender区别和联系

    Unity UE Blender Unity 用户手册 (2019.4 LTS) - Unity 手册 虚幻引擎5.2文档 | 虚幻引擎5.2文档 (unrealengine.com) Blender 3.5 Reference Manual — Blender Manual Blender 是一款免费的开源软件,是一个开源的三维建模和动画软件 Blender: Design and animation platform which assists organizations of all sizes with rendering,

    2024年02月12日
    浏览(32)
  • Laravel一些优雅的写法

    目录 1. 新增商品操作 2. 修改商品操作

    2024年02月07日
    浏览(31)
  • Photoshop图层混合模式公式(Unity,CG实现)

    本文主要目的来自于在unity符合美术在ps里面的演示效果。 两个图层叠加到一起的效果,废话不多说直接看效果: 图片资源 在文章末尾 完整代码 也在末尾 目录 目录 Multiply 正片叠底  Screen 滤色  Color Dodge 颜色减淡  Color Burn 颜色加深 Linear Dodge 线形减淡 Linear Burn 线形加深

    2023年04月22日
    浏览(26)
  • python装13的一些写法

    1. any(** in ** for ** in **) 判断某个集合元素,是否包含某个/某些元素 代码: 输出结果 2. SQL装13的一些写法-CSDN博客

    2024年02月07日
    浏览(28)
  • 游戏开发常用引擎工具介绍对比区别(UE4,Unity,Cocos,LayaAir,[egret-白鹭])

    是一套为开发实时技术而存在的引擎工具。目前广泛应用于3D建模渲染、游戏开发中。它完善的工具套件以及简易的工作流程能够使开发者快速修改或查看成果,对于代码的依赖性很低。而完整公开的源代码则能让使用者自由修改和扩展引擎功能。 是面向开发人员的 3D/2D 游戏

    2024年02月13日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包