[译]Unity3D Shader教程(一)ShaderLab结构

2023-12-20 17:08

本文主要是介绍[译]Unity3D Shader教程(一)ShaderLab结构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 1 Shader结构
  • 2 ShaderLab
    • 2.1 创建Shader
    • 2.2 ShaderLab
    • 2.3 Shader/SubShader/Pass
    • 2.4 Properties and Tags


原文地址
Shader Tutorials by Ronja。


1 Shader结构

在用Shader实现出我们想要的效果前,我们需要先知道Shader代码的基本结构(就像我们用C#编程需要先知道C#的语法一样)。
大多数现代着色器都有一个可变的渲染管线,它至少由一个顶点着色器和一个片段着色器组成,还可以添加几何着色器和曲面细分器,但是这两者我们很少使用。

上面的管线是指什么?可以简单理解为我们工厂中的流水线,那流水线是怎么样的?原材料准备好后,先给工位1的员工进行加工,工位1加工完成后的半成品给工位2的员工继续加工,工位2的员工加工完成后的半成品继续给工位3的员工加工…直到最后一道工序加工完毕,原材料也就变成了产品。那么什么是可变的呢?这里的可变可以理解为一些工位是必需的一些工位不是必需的,可要可不要。比如之前原材料需要经过工位1、工位2、工位3的加工才能做出产品,现在我们把工位2去掉,只留工位1和工位3,但是也能加工出完整的产品。
渲染管线中的顶点着色器和片段着色器就相当于咱们上面流水线的工位1和工位3,这两个在我们的Shader代码中是必需的。而几何着色器和曲面细分器就相当于上面流水线的工位2,虽然它们不是必需的,但是写Shader时用它们可以做出一些特殊的效果,这里不再多说。

顶点着色器(也称为顶点阶段或顶点函数)的作用是将模型的数据变换到屏幕空间下,以便后面的工序(如片元着色器)使用。
模型数据是什么?包括模型网格的顶点的局部坐标、法线方向、uv坐标等。
用什么方法将模型的数据变换到屏幕空间的呢?矩阵变换。由于矩阵变换的原理不是一时半会儿能说清楚的,这就不多说了,暂且记住就行。

除了将模型的数据变换到屏幕空间这一基本功能外,我们可以用自定义的顶点着色器让顶点产生动画而不用我们手动去修改模型的网格,同时可以也将更多数据输出(比如顶点坐标对应的世界坐标等)给片段着色器。经过顶点着色器后,我们得到了屏幕空间上的顶点,顶点之间构成的三角形被光栅化器转换为像素。同时,光栅化器对顶点着色器的输出进行线性的插值,因此网格中间的像素也获得了输出值。在决定渲染哪些像素后,片段着色器(也称为像素着色器)决定像素的颜色。
光栅器的插值过程可以参考下面这张图理解:左边是顶点着色器的输出,右边是光栅器插值后的输出。
光栅化插值过程
大致的管线过程如下:
渲染流水线
从上面我们可以也看出,Shader的作用其实就是以模型的数据为顶点着色器的输入,最后通过片元着色器输出像素颜色。所以Shader代码中必须包含顶点着色器和片元着色器两部分。无论使用什么语言来编写Shader,顶点着色器和片元着色器都是必需的,即使没有顶点和片元阶段概念的基于节点的着色器(如Shader Graph)仍然会在内部生成这些阶段(顶点着色器、片元着色器)。

2 ShaderLab

2.1 创建Shader

Unity 中的着色器代码是一个文本文件,文件名以 .shader 结尾,与 C# 脚本以 .cs 结尾的方式类似。我们可以通过右键单击Assets区域 > Create > Shader > 选择一个模板 来轻松创建其中一个模板。由于我们刚开始学习,所以我们从创建一个最简单的无光照的Shader开始,右键单击 Assets> Create > Shader > Unlit Shader。长下面这个样子。

Shader "Unlit/NewUnlitShader"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);UNITY_TRANSFER_FOG(o,o.vertex);return o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);// apply fogUNITY_APPLY_FOG(i.fogCoord, col);return col;}ENDCG}}
}

2.2 ShaderLab

Unity的Shader是一种被称为“ShaderLab”的自定义语言,它定义了Shader中常用的输入输出数据以及渲染管线中的一些状态,同时用一个代码块将真正的Shader代码(顶点着色器、片元着色器等,用hlsl、glsl或cg编写)包含进来。
上面这句话可能没怎么表达清楚,看图。
在这里插入图片描述
从上面可以看出,真正的ShaderLab现在只占用我们着色器文件的一小部分。它只是以更抽象的形式描述事物,Unity最终会将ShaderLab的部分编译成hlsl、glsl或cg。

2.3 Shader/SubShader/Pass

ShaderLab文件中,由多个大括号 {} 来划分代码块。

其中Shader{}定义了整个着色器,每个Shader文件有且只能有一个Shader{}。Shader后面跟的是此Shader的名字,可以通过斜杠(/)来个各个Shader分门别类。比如我们如下设置

Shader "Tutorials/NewUnlitShader"
{// ...FallBack "VertexLit"
}

则此Shader在Unity菜单栏的位置就如下。
Shader在Menu中的位置
Shder{}代码块中有一个FallBack,其用来定义后备着色器,其作用相当于将后备着色器的所有子着色器粘贴到我们的着色器文件中。我们一般通过添加FallBack "VertexLit"来生成阴影 。为什么用“VertexLit”来生成阴影呢,因为它消耗比较少。
一个Shader{}块可以包含一个或多个SubShader{}。一般编写不同的子着色器供不同的硬件使用,但是关于如何定义以及何时使用哪个子着色器的文档非常缺乏,根据经验,大多数情况使用一个SubShader就已经足够了。
在SubShader中,我们还可以定义SubShader的标签Tags和属性Properties,这些标签和属性将作用于该SubShader中的所有Pass。

Pass 代表一次完整的渲染过程(即一个Pass就表示会完整执行一次渲染流水线,一个Pass绘制一次图像到屏幕上)。如果我们定义了多个Pass,它们将一个接一个地绘制(但URP 只会执行一次Pass)。有多个Pass的着色器被称为多Pass着色器。Pass代码块中可以设置此Pass的名称(可选的),也可以同SubShader一样设置标签Tags和属性(但Pass内的标签和属性只作用于这一个Pass,SubShader中的属性和标签作用于该SubShader中的所有Pass) ,最重要的它包含实际处理渲染的代码(即顶点着色器和片元着色器)。

2.4 Properties and Tags

现在我们来看看上面还没详细说明的Properties和Tags代码块。
Tags标签,用键值对表示。 Subshader 中的Tags标签主要定义带有着色器的材质如何在编辑器中显示,何时渲染或可以对它们应用哪些操作,而 pass 标签主要用于在遗留管道中定义哪些 pass 用于光计算的哪个步骤,SubShader的Tags和Pass的Tags具体可以设置哪些内容可以查看Unity的官方文档。
Properties属性用于在材质编辑器中显示变量。我们一旦更改这些属性的值,使用该材质的物体都将改变。如果我们想每个不同的物体都使用不同的属性,我们得用另外的方法了。由于默认情况下我们可以访问纹理坐标,并且我们可以通过这些属性设置纹理,这样我们可以实现很多效果。我将在下一个文章之一中更深入地解释属性。
总的来说,下面就是着色器的粗略结构。

Shader "Category/Name"{Properties{//Properties}Subshader{Tags{//Subshader Tags}//Settings for all passesPass{Tags{//Pass Tags}//Settings for passCGPROGRAM//shader codeENDCG}}
}

原作者的GitHub。
本文Shader

Shader "Tutorial/001-004_Basic_Unlit"{//show values to edit in inspectorProperties{
//	_Color ("Tint", Color) = (0, 0, 0, 1)
//	_MainTex ("Texture", 2D) = "white" {}}SubShader{//the material is completely non-transparent and is rendered at the same time as the other opaque geometryTags{ "RenderType"="Opaque" "Queue"="Geometry" }Pass{CGPROGRAM
//
//		//include useful shader functions
//		#include "UnityCG.cginc"
//
//		//define vertex and fragment shader functions
//		#pragma vertex vert
//		#pragma fragment frag
//
//		//texture and transforms of the texture
//		sampler2D _MainTex;
//		float4 _MainTex_ST;
//
//		//tint of the texture
//		fixed4 _Color;
//
//		//the mesh data thats read by the vertex shader
//		struct appdata{
//			float4 vertex : POSITION;
//			float2 uv : TEXCOORD0;
//		};
//
//		//the data thats passed from the vertex to the fragment shader and interpolated by the rasterizer
//		struct v2f{
//			float4 position : SV_POSITION;
//			float2 uv : TEXCOORD0;
//		};
//
//		//the vertex shader function
//		v2f vert(appdata v){
//			v2f o;
//			//convert the vertex positions from object space to clip space so they can be rendered correctly
//			o.position = UnityObjectToClipPos(v.vertex);
//			//apply the texture transforms to the UV coordinates and pass them to the v2f struct
//			o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//			return o;
//		}
//
//		//the fragment shader function
//		fixed4 frag(v2f i) : SV_TARGET{
//			//read the texture color at the uv coordinate
//			fixed4 col = tex2D(_MainTex, i.uv);
//			//multiply the texture color and tint color
//			col *= _Color;
//			//return the final color to be drawn on screen
//			return col;
//
//		}ENDCG}}Fallback "VertexLit"
}

ps:感觉翻译得很垃圾啊,捂脸。
博主本文博客链接。

这篇关于[译]Unity3D Shader教程(一)ShaderLab结构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/516802

相关文章

使用Nginx来共享文件的详细教程

《使用Nginx来共享文件的详细教程》有时我们想共享电脑上的某些文件,一个比较方便的做法是,开一个HTTP服务,指向文件所在的目录,这次我们用nginx来实现这个需求,本文将通过代码示例一步步教你使用... 在本教程中,我们将向您展示如何使用开源 Web 服务器 Nginx 设置文件共享服务器步骤 0 —

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

Golang使用minio替代文件系统的实战教程

《Golang使用minio替代文件系统的实战教程》本文讨论项目开发中直接文件系统的限制或不足,接着介绍Minio对象存储的优势,同时给出Golang的实际示例代码,包括初始化客户端、读取minio对... 目录文件系统 vs Minio文件系统不足:对象存储:miniogolang连接Minio配置Min

结构体和联合体的区别及说明

《结构体和联合体的区别及说明》文章主要介绍了C语言中的结构体和联合体,结构体是一种自定义的复合数据类型,可以包含多个成员,每个成员可以是不同的数据类型,联合体是一种特殊的数据结构,可以在内存中共享同一... 目录结构体和联合体的区别1. 结构体(Struct)2. 联合体(Union)3. 联合体与结构体的

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

Linux下MySQL8.0.26安装教程

《Linux下MySQL8.0.26安装教程》文章详细介绍了如何在Linux系统上安装和配置MySQL,包括下载、解压、安装依赖、启动服务、获取默认密码、设置密码、支持远程登录以及创建表,感兴趣的朋友... 目录1.找到官网下载位置1.访问mysql存档2.下载社区版3.百度网盘中2.linux安装配置1.

Python使用pysmb库访问Windows共享文件夹的详细教程

《Python使用pysmb库访问Windows共享文件夹的详细教程》本教程旨在帮助您使用pysmb库,通过SMB(ServerMessageBlock)协议,轻松连接到Windows共享文件夹,并列... 目录前置条件步骤一:导入必要的模块步骤二:配置连接参数步骤三:实例化SMB连接对象并尝试连接步骤四:

Linux使用粘滞位 (t-bit)共享文件的方法教程

《Linux使用粘滞位(t-bit)共享文件的方法教程》在Linux系统中,共享文件是日常管理和协作中的常见任务,而粘滞位(StickyBit或t-bit)是实现共享目录安全性的重要工具之一,本文将... 目录文件共享的常见场景基础概念linux 文件权限粘滞位 (Sticky Bit)设置共享目录并配置粘