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

2023-11-25 07:40

本文主要是介绍[译]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/423390

相关文章

Ubuntu固定虚拟机ip地址的方法教程

《Ubuntu固定虚拟机ip地址的方法教程》本文详细介绍了如何在Ubuntu虚拟机中固定IP地址,包括检查和编辑`/etc/apt/sources.list`文件、更新网络配置文件以及使用Networ... 1、由于虚拟机网络是桥接,所以ip地址会不停地变化,接下来我们就讲述ip如何固定 2、如果apt安

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

PyCharm 接入 DeepSeek最新完整教程

《PyCharm接入DeepSeek最新完整教程》文章介绍了DeepSeek-V3模型的性能提升以及如何在PyCharm中接入和使用DeepSeek进行代码开发,本文通过图文并茂的形式给大家介绍的... 目录DeepSeek-V3效果演示创建API Key在PyCharm中下载Continue插件配置Con

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创

Spring Boot整合log4j2日志配置的详细教程

《SpringBoot整合log4j2日志配置的详细教程》:本文主要介绍SpringBoot项目中整合Log4j2日志框架的步骤和配置,包括常用日志框架的比较、配置参数介绍、Log4j2配置详解... 目录前言一、常用日志框架二、配置参数介绍1. 日志级别2. 输出形式3. 日志格式3.1 PatternL

使用Navicat工具比对两个数据库所有表结构的差异案例详解

《使用Navicat工具比对两个数据库所有表结构的差异案例详解》:本文主要介绍如何使用Navicat工具对比两个数据库test_old和test_new,并生成相应的DDLSQL语句,以便将te... 目录概要案例一、如图两个数据库test_old和test_new进行比较:二、开始比较总结概要公司存在多

MySQL8.2.0安装教程分享

《MySQL8.2.0安装教程分享》这篇文章详细介绍了如何在Windows系统上安装MySQL数据库软件,包括下载、安装、配置和设置环境变量的步骤... 目录mysql的安装图文1.python访问网址2javascript.点击3.进入Downloads向下滑动4.选择Community Server5.

CentOS系统Maven安装教程分享

《CentOS系统Maven安装教程分享》本文介绍了如何在CentOS系统中安装Maven,并提供了一个简单的实际应用案例,安装Maven需要先安装Java和设置环境变量,Maven可以自动管理项目的... 目录准备工作下载并安装Maven常见问题及解决方法实际应用案例总结Maven是一个流行的项目管理工具

本地私有化部署DeepSeek模型的详细教程

《本地私有化部署DeepSeek模型的详细教程》DeepSeek模型是一种强大的语言模型,本地私有化部署可以让用户在自己的环境中安全、高效地使用该模型,避免数据传输到外部带来的安全风险,同时也能根据自... 目录一、引言二、环境准备(一)硬件要求(二)软件要求(三)创建虚拟环境三、安装依赖库四、获取 Dee