【UnityShader入门精要学习笔记】第十七章 表面着色器

news2024/11/17 2:30:51

在这里插入图片描述
本系列为作者学习UnityShader入门精要而作的笔记,内容将包括:

  • 书本中句子照抄 + 个人批注
  • 项目源码
  • 一堆新手会犯的错误
  • 潜在的太监断更,有始无终

我的GitHub仓库

总之适用于同样开始学习Shader的同学们进行有取舍的参考。


文章目录

  • 表面着色器
  • 表面着色器的一个例子
  • 编译指令
    • 表面函数
    • 光照模型
    • 其他可选参数
  • 两个结构体
    • 数据来源:Input 结构体
    • 表面属性:SurfaceOutput结构体
  • Unity背后做了什么
  • 表面着色器实例分析


浅看了第十八章,感觉没必要写。所以这一章表面着色器就是我们的最终章了,接下来重心将会落实到一些深入引擎和优化技术上。

表面着色器

顶点片元着色器本质上是一种对硬件友好的方式,但是对人类不友好。虽然计算机世界中这样的例子已经比比皆是了。出于拒绝反人类的目的,一种新的着色器表面着色器(Surface Shader) 被加入到Unity中。

表面着色shader包含了3个层次:表面着色器,光照模型和光照着色器

其中表面着色器定义了模型表面的反射率、法线和高光等,光照模型则选择使用的光照模型类型,是半兰伯特?还是BlinnPhong或者其他光照模型?光照着色器则由系统进行实现。

表面着色器大大减少了shader开发的工作量,大多数时候我们只需要和表面着色器打交道,定义一些属性,选择要使用的光照模型即可。

表面着色器实际上就是对顶点片元着色器上的一层抽象,使用表面着色器可以用一种更容易理解的方式编写shader,不需要考虑前向渲染路径还是延迟渲染路径,场景中的光源等等等等要素。


表面着色器的一个例子

要实现一个前向光照渲染的材质,我们要定义光照模型,定义前向渲染Pass,定义阴影pass。总而言之,如果用顶点片元着色器实现会很难,很复杂。

但是现在我们可以使用表面着色器来直接实现:

Shader "Custom/BumpedDiffuse_Copy"
{
    Properties
    {
        _Color("Main Color",Color) = (1,1,1,1)
        _MainTex("Base",2D) = "white"{}
        _BumpMap("Normalmap",2D) = "bump"{}
    }
    SubShader
    {
        Tags{"RenderType" = "Opaque"}
        LOD 300
        CGPROGRAM
        #pragma surface surf Lambert
        #pragma target 3.0
        sampler2D _MainTex;
        sampler2D _BumpMap;
        fixed4 _Color;

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
        };
        void surf(Input IN,inout SurfaceOutput o)
        {
            fixed4 tex = tex2D(_MainTex,IN.uv_MainTex);
            o.Albedo = tex.rgb * _Color.rgb;
            o.Alpha = tex.a * _Color.a;
            o.Normal = UnpackNormal(tex2D(_BumpMap,IN.uv_BumpMap));
        }
        ENDCG
    }
    Fallback "Diffuse"
}

如此简单就能完成一个光照模型,与顶点片元着色器需要包含到一个特定的Pass块中不同, 表面着色器可以直接且必须写在SubShader块中,Unity会自动生成多个Pass,并在CGPROGRAM块中定义表面着色器的具体代码。


编译指令

在表面着色器中,我们通过编译指令来和Unity进行沟通,编译指令最重要的作用是指示该表面着色器使用的表面函数和光照函数,并设置一些可选参数。表面着色器的CG块中的第一句代码往往就是它的编译指令,编译指令的一般格式如下:

#pragma surface surfaceFunction lightModel [optionalparams]

指定表面函数和光照模型,以及其他的一些可选参数来控制表面着色器的一些行为。

表面函数

表面着色器抽象出了表面这一概念,并包含了表面属性,一个对象的表面属性定义了它的反射率、光滑度、透明度等值。而编译指令中的surfaceFunction就用于定义这些表面属性。surfaceFunction通常就是名为surf的函数(函数名可以任意),它的函数格式是固定的:

void surf(Input IN, inout SurfaceOutput o)
void surf(Input IN, inout SurfaceOutputStandard o)
void surf(Input IN, inout SurfaceOutputStandardSpecular o)

其中输入结构体Input是我们自定义的,用于设置各种表面属性。

输出结构体通常是SurfaceOutput ,SurfaceOutputStandard ,SurfaceOutputStandardSpecular ,这些类型,它们是unity内置的结构体,需要配合不同的光照模型使用。

光照模型

官方自定义光照文档

除了表面函数,我们还需要指定光照函数,光照函数会使用表面函数中设置的各种表面属性来应用某些光照模型。

unity中内置了基于物理的光照模型Standard和StandardSpecular,以及简单的非基于物理的Lambert和BlinnPhong

当然我们也可以定义自己的光照函数,例如用下列函数来定义前线渲染中的光照函数:

// 根据官方文档的代码,Lighting是统一的前缀,后面字符部分才是光照模型的名称
// 一些常用的变量直接按规定属性名定义为函数入参并使用即可
// 用于不依赖视角的光照模型,例如漫反射
half4 Lighting<Name> (SurfaceOutput s,half3 lightDir, half atten);
// 用于依赖视角的光照模型,例如高光反射
half4 Lighting<Name> (SurfaceOutput s,half3 lightDir, half3 virwDir, half atten);

其他可选参数

除了光照模型和表面着色器两个必需参数之外,我们还可以设置一些可选参数

这些参数都在官方文档中记载了

简单的使用可选参数就可以定义一些功能

注意表面着色器只能在内置渲染管线Build-In Pipeline中使用,而URP和HDRP是不能使用的,在URP和HDRP中想要简单的实现Shader需要使用Shader Graph


两个结构体

表面着色器最多支持自定义4种关键的函数:

  • 表面函数(用于设置各种表面性质,如反射率,法线等)
  • 光照函数(定义表面使用的光照模型)
  • 顶点修改函数(修改或传递顶点属性)
  • 最后的颜色修改函数(对最后的颜色进行修改)

那么,这些函数之间的信息传递是如何实现的呢?

一个表面着色器需要使用两个结构体:表面函数的输入结构体Input,以及存储了表面属性的结构体SurfaceOutput

数据来源:Input 结构体

Input结构体包含了许多表面属性的数据来源,因此,它会作为表面函数的输入结构体。Input支持很多内置的变量名,通过这些变量名,我们告诉Unity需要使用的数据信息,例如,在Input结构体种包含了主纹理和法线纹理的采样坐标uv_MainTex和uv_BumpMap。这些采样坐标必须以uv为前缀

因此纹理的采样直接用uv_sample2Dname这种格式定义即可,而其他变量需要根据下表严格定义!

在这里插入图片描述

只需要定义变量即可使用了

除了这些变量之外,如果我们想要自定义变量也可以,就像顶点片元着色器种实现的一样,自定义变量并在顶点修改函数中进行处理,将其传递到surface函数中去。


表面属性:SurfaceOutput结构体

SurfaceOutput就是专门用于存储表面属性的。SurfaceOutput ,SurfaceOutputStandard ,SurfaceOutputStandardSpecular 等,它们作为表面着色器的输出,也作为光照函数的输入。这些结构体的变量都是提前声明好的,直接使用即可。
在这里插入图片描述

剩下的就是光照模型,可以用内置的光照模型,我们也可以自定义光照函数
在这里插入图片描述


Unity背后做了什么

使用表面着色器,我们只需要编译指令、自定义函数和两个结构体就可以生成一个表面着色器。而实际上Unity在背后为表面着色器生成了一系列的顶点/片元着色器

Unity在背后会根据表面着色器生成一个包含了很多Pass的顶点/片元着色器,例如我们设置了不同的渲染路径则会生成对应渲染路径的Pass,我们设置了不同LightMode则会生成不同的对应光照的Pass,若使用了addshadow则会生成ShadowCaster的阴影Pass。
在这里插入图片描述
我们只需在Unity 的表面着色器的面板上点击Show generated code即可生成对应的顶点/片元着色器代码。

在这里插入图片描述
根据上图可以看到,其实表面着色器的过程很简单。我们之前定义的部分都是属于片元着色器中的自定义部分。

Unity对Pass的自动生成过程如下:

(1)直接将表面着色器中CGPROGRAM块部分的代码赋值并解析,这部分代码包括了我们对预编译指令,定义的变量,以及表面函数光照函数等。这些函数和变量将在处理后进行调用

(2)unity分析代码,并生成顶点着色器的输出v2f_surf结构体,用于顶点着色器和片元着色器之间的变量传递。 v2f_surf结构体中的变量是根据我们定义的相应变量生成的,注意变量名称要一模一样。若某些变量在编译时发现未使用,也不会被生成带v2f_surf结构体中。

(3)接着生成顶点着色器vert_surf

  • 若我们自定义了顶点修改函数,则unity会首先调用顶点修改函数来修改顶点数据,或填充自定义的Input结构体中的变量,然后Unity会分析顶点修改函数中修改的数据,并通过Input结构体将修改结果存储到v2f_surf相应的变量中。
  • 将顶点数据计算成一些其他的通用变量,例如顶点坐标,纹理坐标,法线方向,逐顶点光照,光照纹理的采样坐标等。可以通过编译器控制某些变量是否需要计算
  • 最后将v2f_surf传递到下一片元着色器中

(4)生成片元着色器frag_surf

  • 使用顶点着色器传递的v2f_surf结构体变量来填充Input结构体,例如纹理坐标,视角方向等
  • 调用自定义的表面函数填充SurfaceOutput结构体
  • 调用光照函数得到初始的颜色值,如果使用的是内置的Lambert和BlinnPhong光照模型,还会计算动态全局光照
  • 进行其他的颜色叠加,例如若没有使用光照烘焙,还会添加逐顶点光照
  • 最后,如果自定义了最后的颜色修改函数,unity会调用它进行最后的颜色修改

表面着色器实例分析

文中的表面着色器实例分析针对的是表面着色器生成的顶点/片元着色器代码,具体可以详见书本

我们此处简单分析一下表面着色器的代码:

Shader "Unity Shaders Book/Chapter 17/Normal Extrusion" {
	Properties {
		_ColorTint ("Color Tint", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BumpMap ("Normalmap", 2D) = "bump" {}
		_Amount ("Extrusion Amount", Range(-0.5, 0.5)) = 0.1
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 300
		
		CGPROGRAM
		
		// surf - which surface function.
		// 自定义的光照模型
		// CustomLambert - which lighting model to use.
		// 自定义的顶点修改函数
		// vertex:myvert - use custom vertex modification function.
		// 自定义的颜色修改函数
		// finalcolor:mycolor - use custom final color modification function.
		// 编译选项——生成阴影
		// addshadow - generate a shadow caster pass. Because we modify the vertex position, the shder needs special shadows handling.
		// 编译选项——不为deferred/legacy deferred渲染路径生成pass
		// exclude_path:deferred/exclude_path:prepas - do not generate passes for deferred/legacy deferred rendering path.
		// 不生成用于全局动态光照的“meta” pass
		// nometa - do not generate a “meta” pass (that’s used by lightmapping & dynamic global illumination to extract surface information).
		#pragma surface surf CustomLambert vertex:myvert finalcolor:mycolor addshadow exclude_path:deferred exclude_path:prepass nometa
		#pragma target 3.0
		
		fixed4 _ColorTint;
		sampler2D _MainTex;
		sampler2D _BumpMap;
		half _Amount;
		
		struct Input {
			float2 uv_MainTex;
			float2 uv_BumpMap;
		};

		// 自定义顶点修改函数
		void myvert (inout appdata_full v) {
			v.vertex.xyz += v.normal * _Amount;
		}

		void surf (Input IN, inout SurfaceOutput o) {
			fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
			o.Albedo = tex.rgb;
			o.Alpha = tex.a;
			o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
		}

		// 自定义的光照模型
		half4 LightingCustomLambert (SurfaceOutput s, half3 lightDir, half atten) {
			half NdotL = dot(s.Normal, lightDir);
			half4 c;
			c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
			c.a = s.Alpha;
			return c;
		}

		// 自定义的颜色修改函数
		void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) {
			color *= _ColorTint;
		}
		
		ENDCG
	}
	FallBack "Legacy Shaders/Diffuse"
}

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

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

相关文章

274 基于matlab的随机粗糙表面对微气体轴承内气体压强分布的影响

基于matlab的随机粗糙表面对微气体轴承内气体压强分布的影响。采用差分法求解气体轴承的雷诺方程&#xff0c;通过尺寸参数、分形维数对粗糙度表面设置&#xff0c;滑流参数设置&#xff0c;实现气压分布可视化结果显示。程序已调通&#xff0c;可直接运行。 274 气体轴承 随机…

SpringCloud-面试篇(二十三)

&#xff08;1&#xff09;SpringCloud常见组件有那些 有无数微服务需要相互调用&#xff1a;可以用远程调用组件OpenFeign组件&#xff0c;也可以用Dobble 这么多微服务相互调用怎么管理&#xff1a;就用到注册中心组件Nacos&#xff0c;Eureka 所有的服务去找注册中心做注…

【Vue3-Element-Admin 动态路由】涉及到的配置

Vue3-Element-Admin 动态路由 涉及到的配置 0. Vue3-Element-Admin 项目地址1. router/index.ts2. Mock接口模拟数据3. store/permission4. api/menu5. plugins/permission 这篇文章讲的主要是 Vue3-Element-Admin 差不多内置的动态路由配置 (根据后端接口渲染) 先把开发环境&a…

vue3+three.js给glb模型设置视频贴图

1.在网上下载一个显示屏或者自己画一个,在blender中设置好显示屏的Mesh,UV设置好,这样方便代码中添加纹理贴图。可以让美术在建模软件中,先随机设置一张图片作为纹理,验证UV是否设置好 关于如何 在blender中给模型设置UV贴图百度很多的 // 视频 import * as THREE from…

直播回顾丨GQL 与新版本悦数图数据库亮点解析

5 月 23 日&#xff0c;悦数图数据库产品总监方扬亲临直播间&#xff0c;为我们深入剖析了 GQL 的技术内核&#xff0c;以及它如何引领图数据库技术的全新变革。同时&#xff0c;还揭秘了新版悦数图数据库的众多技术特点&#xff0c;让人眼前一亮。 添加图片注释&#xff0c;不…

python的模块

什么是模块&#xff08;Module&#xff09; 在计算机程序的开发过程中&#xff0c;随着程序代码越写越多&#xff0c;写在一个文件里的代码就会越来越长&#xff0c;越来越不容易维护。 为了让代码方便维护&#xff0c;我们将代码进行分类&#xff0c;分别放到不同的文件里。…

Xcode中给UIView在xib中添加可视化的属性

给UIView在xib中添加可视化的属性 效果如下图&#xff1a; 可以直接设置view 的 borderColor 、borderWidth、cornerRadius&#xff0c;也可以单独指定view的某个角是圆角。减少了代码中的属性。 完整代码&#xff1a; UIViewBorder.h #import <UIKit/UIKit.h>inter…

Python - 深度学习系列38 重塑实体识别5-预测并行化改造

说明 在重塑实体识别4中梳理了数据流&#xff0c;然后我发现pipeline的串行效率太低了&#xff0c;所以做了并行化改造。里面还是有不少坑的&#xff0c;记录一下。 内容 1 pipeline 官方的pipeline看起来的确是比较好用的&#xff0c;主要是实现了比较好的数据预处理。因为…

【全网唯一】触摸精灵iOS版纯离线本地文字识别插件

目的 触摸精灵iOS是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。但触摸精灵的图色功能比较单一&#xff0c;无法识别屏幕上的图像&#xff0c;根据图像的变化自动执行相应的操作。本篇文章主要…

电脑没电关机,wsl和docker又挂了,附解决过程

如题&#xff0c;开了个会没带笔记本电源&#xff0c;点啊弄关机后docker打不开&#xff0c;我以为是docker坏了&#xff0c;结果docker报错&#xff1a; An unexpected error occurred while executing a WSL command. Either shut down WSL down with wsl --shutdown, and/or…

day32--Spring(一)

一、Spring简介 1 Spring课程介绍 问题导入 我们为什么要学习Spring框架&#xff1f; 1.1 为什么要学 Spring技术是JavaEE开发必备技能&#xff0c;企业开发技术选型命中率>90% 专业角度 简化开发&#xff0c;降低企业级开发的复杂性框架整合&#xff0c;高效整合其他技…

概率分布、回归分析、假设检验……用 DolphinDB 函数库快速实现概率统计分析

在金融和物联网等领域&#xff0c;概率统计与分析扮演着至关重要的角色。DolphinDB 作为一款强大的时序数据库&#xff0c;提供了一系列内置的概率统计与分析函数&#xff0c;能够满足用户的各种需求。 金融领域 风险管理&#xff1a;通过概率统计分析&#xff0c;金融机构可…

python数据分析——逻辑回归

参考资料&#xff1a;活用pandas库 逻辑回归 当响应变量为二值响应变量时&#xff0c;经常使用逻辑回归对数据建模。 # 导入pandas库 import pandas as pd # 导入数据集 acspd.read_csv(r"...\data\acs_ny.csv") # 展示数据列 print(acs.columns) # 展示数据集 pri…

进程间通信(27000字超详解)

&#x1f30e;进程间通信 文章目录&#xff1a; 进程间通信 进程间通信简介       进程间通信目的       初识进程间通信       进程间通信的分类 匿名管道通信       认识管道       匿名管道       匿名管道测试       管道的四种…

免费,Scratch蓝桥杯比赛历年真题--第15届蓝桥杯STEMA真题-2024年3月份(含答案解析和代码)

第15届蓝桥杯STEMA真题-2024年3月份 一、单选题 答案&#xff1a;D 解析&#xff1a;y坐标正值表示上&#xff0c;负值表示下&#xff0c;故答案为D。 答案&#xff1a;C 解析&#xff1a;18<25为真&#xff0c;或关系表示一真即为真&#xff0c;故答案为C。 答案&#xff…

2024蓝桥杯初赛决赛pwn题全解

蓝桥杯初赛决赛pwn题解 初赛第一题第二题 决赛getting_startedbabyheap 初赛 第一题 有system函数&#xff0c;并且能在bss上读入字符 而且存在栈溢出&#xff0c;只要过掉check函数即可 check函数中&#xff0c;主要是对system常规获取权限的参数&#xff0c;进行了过滤&…

linux部署运维3——centos7下导入导出mysql数据库的sql文件以及查询数据量最大的表信息

在实际项目开发或者项目运维过程中&#xff0c;数据库的导入导出操作比较频繁&#xff0c;如果可以借助第三方工具那当然算喜事一桩&#xff1b;但是如果不允许外部访问&#xff0c;那么就只能使用数据库自带的命令&#xff0c;也是相当方便的。 一.导入sql文件 1.在linux命令…

Asp.Net Core 实现分片下载的最简单方式

技术群里的朋友遇到了这个问题&#xff0c;起初的原因是他对文件增加了一个属性配置 fileResult.EnableRangeProcessing true;这个属性我从未遇到过&#xff0c;然后&#xff0c;去F1查看这个属性的描述信息也依然少的可怜&#xff0c;只有简单的描述为(获取或设置为 启用范围…

CCIG 2024:大模型技术及其前沿应用论坛深度解析

一、CCIG论坛介绍 中国图象图形大会&#xff08;CCIG 2024&#xff09;是一场备受瞩目的学术盛会&#xff0c;近期在陕西省西安市曲江国际会议中心举行。这次会议以“图聚智生&#xff0c;象合慧成”为主题&#xff0c;由中国图象图形学学会主办&#xff0c;旨在汇聚图像图形领…

一篇文章讲透数据结构之树and二叉树

一.树 1.1树的定义 树是一种非线性的数据结构&#xff0c;它是有n个有限结点组成的一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根在上&#xff0c;叶在下的。 在树中有一个特殊的结点&#xff0c;称为根结点&#xff0c;根结点…