UnityShader(四)一个最简单的顶点/片元着色器

目录

顶点/片元着色器的基本结构:

简单的例子

增加模型数据

顶点着色器和片元着色器之间的通信


顶点/片元着色器的基本结构:

Shader "MyShaderName"{Properties{//属性}SubShader{//针对显卡A的SubShaderPass{//设置渲染状态和标签//开始Cg代码片段CGPROGRAM//该代码片段的编译指令,例如:#pragma vertex vert#pragma fragment frag//Cg代码写在这里ENDCG//其他设置}//其他需要的Pass}SubShader{//针对显卡B的SubShader}Fallback "VertexLit"
}

它包含了Shader、Properties、SubShader、Fallback等语义块。

其中最重要的部分就是Pass语义块,绝大部分的代码都是写在这个语义块中的。

简单的例子

Unity版本2021.3.12

创建一个新的场景,去除天空盒(菜单栏中,选择Window->Rendering->Lighting->-Environment>SkyboxMaterial,把该项设置为空)

创建一个UnityShader【Unlit Shader】并取名SimpleShader

创建一个材质并重命名为SimpleShaderMat,赋予SimpleShader

创建一个球体并将SimpleShaderMat材质赋给它

打开SimpleShader并删除所以代码,编写以下代码

Shader "Unity Shaders Book/Simple Shader"{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragfloat4 vert(float4 v: POSITION):SV_POSITION{return mul(UNITY_MATRIX_MVP,v);}float4 frag():SV_Target{return fixed4(1.0,1.0,1.0,1.0);}ENDCG}}
}

效果:

代码解释:

在第一行通过Shader语义定义了这个Shader的名字,保持良好的命名习惯可以帮助我们在为材质球选择Shader时快速找到自定义的Shader。

需要注意的是我们并没有使用Properties语义,因为这不是必需的。

然后我们声明了SubShader和Pass语义块,在这个例子中我们不需要进行任何渲染设置和标签设置,因此SubShader将使用默认的渲染设置和标签设置。在SubShader中我们定义了一个Pass,这个Pass里我们同样没有设置任何的渲染设置和标签设置。

接着就是CGPROGRAM和ENDCG所包围的CG代码,这里有两个非常重要的编译指令:

#pragma vertex vert

#pragma fragment frag

它们将告诉Unity哪个函数包含了顶点着色器的代码,哪个函数包含了片元着色器的代码。

通用的编译指令如下:

#pragma vertex name

#pragma fragment name

其中name就是我们指定的函数名,这个函数名不一定是vert和frag,它们可以是任意自定义的函数名(一般我们使用vert和frag这两个函数名,因为它们很直观,但这不是必须的)

我们看一下vert函数的定义:

    float4 vert ( float4 v: POSITION ):SV_POSITION {
                return UnityObjectToClipPos(v);
            }

这是我们在这个例子中使用的顶点着色器代码,它是逐顶点执行的。vert函数的输入v包含了这个顶点的位置,这是我们通过POSITION语义指定的。它的返回值是一个float4类型的变量,它是该顶点在裁剪空间中的位置,POSITIONSV_POSITION都是Cg/HLSL中的语义

在这个例子中,POSITION将告诉Unity把模型的顶点坐标填充到输入v中,SV_POSITION将告诉Unity顶点着色器的输出是裁剪空间中的顶点坐标。

如果没有这些语义来限定输入和输出参数,渲染器就完全不知道用户的输入和输出是什么,就会得到错误的效果。

顶点着色器只包含了一行代码,这一步就是把顶点坐标从模型空间转换到裁剪空间中,UNITY_MATRIX_MVP矩阵是Unity内置的模型.观察.投影矩阵

我们再看一下frag函数的定义:

   float4 frag():SV_Target{
                return fixed4(1.0,1.0,1.0,1.0);
            }

在这个例子中,frag函数没有任何的输入,它的输出是一个float4类型的变量,并且使用了SV_Target语义进行限定。

SV_TargetHLSL中的一个系统语义,等同于告诉渲染器,把用户的输出颜色存储到一个渲染目标中,这里将输出到默认的帧缓存中。

片元着色器返回一个表示白色的fixed4类型的变量,片元着色器输出的颜色的每个分量范围在[0,1],其中(0,0,0)表示黑色(1,1,1)表示白色

注:

编写完代码后,高版本会出现警告和以下修改建议,会自动修改可不管,代码会自动修改成UnityObjectToClipPos(*)的格式

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

增加模型数据

模型数据从哪里来的?

在上面的例子中,顶点着色器我们使用了POSITION语义得到了模型的顶点位置,如果需要增加更多的模型数据该如何操作呢?

比如现在我们想要得到模型上每个顶点的纹理坐标和法线方向(这个需求很常见,我们需要使用纹理坐标来访问纹理,法线可以用来计算光照),我们可以使用一个结构体来重新定义我们的输入参数v

修改代码如下:

Shader "Unity Shaders Book/Simple Shader"{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag//使用一个结构体来定义顶点着色器的输入struct a2v{//POSITION语义告诉Unity用模型空间的顶点坐标填充vertex变量float4 vertex:POSITION;//NORMAL语义告诉Unity用模型空间的法线方向填充normal变量float3 normal:NORMAL;//TEXCOORF0语义告诉Unity用模型的第一套纹理坐标填充texcoord变量float4 texcoord:TEXCOORD0;};float4 vert(a2v v: POSITION):SV_POSITION{return mul (UNITY_MATRIX_MVP,v.vertex);}float4 frag():SV_Target{return fixed4(1.0,1.0,1.0,1.0);}ENDCG}}
}

在上面的代码中我们声明了一个新的结构体av2,它包含了顶点着色器所需要的模型数据。

在结构体中我们用到了其他的Unity支持的语义,当它们作为顶点着色器的输入时都是有特定含义的,因为Unity会根据这些语义来填充这个结构体,对于顶点着色器的输入,Unity支持的语义有:POSITION,TANGENT,NORMAL,TEXCOORD0,TEXCOORD1,TEXCOORD2,TEXCOORD3,COLOR等。

为了创建一个新的结构体,必须使用下面的格式定义它:

struct StructName{

        Type Name:Semantic;

        Type Name:Semantic;

        ......

};

其中Semantic(语义)是不能够省略的。

在上面的代码中我们还修改了vert函数的输入参数类型,通过修改为我们自定义的结构体类型我们就可以在顶点着色器中访问模型数据了。

(扩展阅读:填充到POSITION,NORMAL,这些语义中的数据是由使用了该材质的Mesh Render组件提供的。在每帧调用Draw Call时,Mesh Render组件会把它负责渲染的模型数据发送个UnityShader。一个模型通常包含了一组三角面片,每个三角面片由三个顶点构成,而每个顶点又包含了一些数据【比如顶点位置,法线,切线,纹理坐标,顶点颜色等】) 

顶点着色器和片元着色器之间的通信

在实践中,我们往往希望从顶点着色器输出一些数据(比如模型的法线,纹理坐标等)传递给片元着色器,这就涉及到顶点着色器和片元着色器之间的通信。

为此我们需要定义一个新的结构体,修改代码如下:

Shader "Unity Shaders Book/Simple Shader"{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag//使用一个结构体来定义顶点着色器的输入struct a2v {//POSITION语义告诉Unity用模型空间的顶点坐标填充vertex变量float4 vertex:POSITION;//NORMAL语义告诉Unity用模型空间的法线方向填充normal变量float3 normal:NORMAL;//TEXCOORF0语义告诉Unity用模型的第一套纹理坐标填充texcoord变量float4 texcoord:TEXCOORD0;};//使用一个结构体来定义顶点着色器的输入struct v2f{//SV_POSITION语义告诉Unity,pos里包含了顶点在裁剪空间中的位置信息float4 pos:SV_POSITION;//COLOR0语义可以用于存储颜色信息fixed3 color:COLOR0;};v2f vert(a2v v){//声明输出结构v2f o;o.pos=mul(UNITY_MATRIX_MVP,v.vertex);//v.normal包含了顶点的法线方向,其分量范围在【-1.0,1.0】//下面的代码把分量范围映射到了[0.0,1.0]//存储到o.color中传递给片元着色器o.color=v.normal*0.5+fixed3(0.5,0.5,0.5);return o;}float4 frag(v2f i):SV_Target{//将插值后的i.color显示到屏幕上return fixed4(i.color,1.0);}ENDCG}}}

效果如下:

代码解释:

上面代码中我们声明了一个新的结构体v2f,用于在顶点着色器和片元着色器之间传递信息。同样结构体v2f也需要指定每个变量的语义。我们使用了SV_POSITIONCOLOR0语义。

顶点着色器的输出结构中,必须包含一个变量,它的语义时SV_POSITION,否则渲染器将无法得到裁剪空间中的顶点坐标,也就无法将顶点渲染到屏幕上。COLOR0语义中的数据可以由用户自行定义,但一般都是存储颜色(例如逐顶点的漫反射颜色或者逐顶点的高光反射颜色)。

需要注意的是,顶点着色器时逐顶点调用,而片元着色器是逐片元调用,片元着色器中的输入实际上是把顶点着色器的输出进行插值后得到的结果。

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

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

相关文章

C++正则表达式全攻略:从基础到高级应用

C正则表达式全攻略:从基础到高级应用 一、基础知识二、正则表达式的基本匹配三、C中使用正则表达式四、高级正则表达式五、实践示例六、性能优化6.1、编译正则表达式6.2、避免过度使用回溯6.3、优化匹配算法 七、总结 一、基础知识 正则表达式是一种用于匹配、搜索…

ORACLE Primavera P6, Unifier v23.12 系统分享

引言 根据上周的计划,我近日简单制作了一个基于ORACLE Primavera P6 EPPM 以及Unifier 最新版23.12的虚拟机演示环境,里面包括了p6 和 unifier的全套系统服务 此虚拟系统环境仅用于演示、培训和测试目的。如要在生产环境中使用此虚拟机,请您…

系列二、RestTemplate简介

一、RestTemplate简介 1.1、概述 RestTemplate是一种便捷的访问RestFul服务的模板类,是Spring提供的用于访问Rest服务的客户端模板工具集,它提供了多种便捷访问远程HTTP服务的方法。 1.2、API https://docs.spring.io/spring-framework/docs/5.2.2.REL…

从马尔可夫奖励过程到马尔可夫决策到强化学习【02/2】

一、说明 随着 Open AI 于 2023 年 11 月 6 日发布GPT 代理,我们所有人都对它带来的支持和灵活性着迷。想象一下,有一个个性化的数字助手始终在您身边,根据您的喜好完成日常平凡任务或艰巨任务。但为这些定制代理提供动力的是强化学习&#x…

【C语言】Windows上用GTK写GUI程序

要使用GTK开发一个Windows图形用户界面程序,需要首先设置GTK开发环境。这通常包括安装GTK库和它的依赖,以及配置编译器和工具链。可以选择使用纯C语言和GTK库或者使用支持GTK绑定的其他语言,如Python、C或Rust。 1. 安装GTK开发库 在Window…

Prototype原型模式(创建对象)

原型模式:Prototype 链接:原型模式实例代码 注解 模式定义 使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。 ——《设计模式》GoF 目的 在软件系统中,经常面临这“某些结构复杂的对象”的创建工作&am…

Java ArrayList解密

数组的大小是固定的,一旦创建的时候指定了大小,就不能再调整了。也就是说,如果数组满了,就不能再添加任何元素了。 ArrayList 在数组的基础上实现了自动扩容,并且提供了比数组更丰富的预定义方法(各种增删改…

论文阅读: AAAI 2022行人重识别方向论文-PFD_Net

本篇博客用于记录一篇行人重识别方向的论文所提出的优化方法《Pose-Guided Feature Disentangling for Occluded Person Re-identification Based on Transformer》,论文中提出的PDF_Net模型的backbone是采用《TransReID: Transformer-based Object Re-Identificati…

TypeScript 之 interface 和 type 的区别

结论: 1、可以声明的数据类型 type 可以修饰任何类型 (值类型和引用数据类型) interface 只能修饰引用类型 (对象、数组、函数) //interface 声明对象属性 interface ins {a: string;b?: number; //可选项 }// int…

HackTheBox - Medium - Linux - Encoding

Encoding 前言 经过10个月左右的网安自学,我想说的第一句话无疑是:感谢TryHackMe。当然,后续的HackTheBox&学院、CRTO等等,对我的帮助都很大。 许多师傅们都在年度总结,我也看了大家都收获很多,都很…

Docker容器基础知识点总结

一 、Docker架构 dockers加速镜像&#xff1a; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": ["https://z90yxq2m.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restar…

MySQL基础学习: linux系统mysql 密码插件 validate_password安装

1、没有安装mysql密码插件&#xff0c;执行命令&#xff1a;SHOW VARIABLES LIKE ‘validate_password%’; 2、安装mysql密码插件&#xff0c;执行命令&#xff1a;install plugin validate_password soname ‘validate_password.so’; 3、再次执行&#xff1a;SHOW VARIABLE…

【Unity入门】MenuItem 和 ContextMenu 的使用方法

目录 一、ContextMenu描述使用示例ContextMenuItem使用示例 二、MenuItem描述使用示例 三、MenuItem 和 ContextMenu 的区别 一、ContextMenu 描述 ContextMenu 属性用于向上下文菜单添加命令。 在该附加脚本的 Inspector 中&#xff0c;当用户选择该上下文菜单时&#xff0c…

【Spring实战】16 Profile

文章目录 1. 定义2. 使用2.1 定义 Profile2.2 激活 Profile 3. 演示3.1 properties文件3.2 打印日志3.3 启动服务&验证3.4 修改 active3.5 重启服务&验证 4. 应用场景4.1 数据库配置4.2 日志配置 5. 代码详细总结 Spring 框架提供了一种强大的机制&#xff0c;允许在不…

docker 在线安装mysql 8.0.21版本

1、拉取mysql 8.0.21版本镜像 2、查看镜像 docker images 3、在宿主机 /usr/local/mysql 下的 conf 文件夹下&#xff0c;创建 my.cnf 文件&#xff0c;并编辑内容 [mysql] default-character-setutf8 [client] port3306 default-character-setutf8 [mysqld] port3306 se…

普中STM32-PZ6806L 使用FlyMcu串口烧录程序

简介 我的串口下载电路坏掉了, 所以研究了下如何通过USB转TTL进行程序的下载, 为后续Bootloader部分做准备;连接 我的板几乎是十年前买的&#xff0c; 所以电路与现有网上的资料有些差异, 所以仅供参考 USB 转 TTL线 与开发板 连接&#xff0c; 如图图中 ①, 需要去掉第一个…

YOLOv8训练损失、mAP画图功能 | 支持多结果对比,多结果绘在一个图片(科研必备)

一、本文介绍 本文给大家带来的是YOLOv8系列的绘图功能&#xff0c;我将向大家介绍YOLO系列的绘图功能。我们在进行实验时&#xff0c;经常需要比较多个结果&#xff0c;针对这一问题&#xff0c;我写了点代码来解决这个问题&#xff0c;它可以根据训练结果绘制损失(loss)和mA…

python实现平滑线性滤波器——数字图像处理

原理&#xff1a; 平滑线性滤波器是一种在图像处理中广泛使用的工具&#xff0c;主要用于降低图像噪声或模糊细节。这些滤波器的核心原理基于对图像中每个像素及其邻域像素的线性组合。 邻域平均&#xff1a; 平滑线性滤波器通过对目标像素及其周围邻域像素的强度值取平均来工…

Openwrt修改Dropbear ssh root密码

使用ssh工具连接路由器 输入&#xff1a;passwd root 输入新密码 重复新密码 设置完成 rootImmortalWrt:~# passwd root Changing password for root New password:

dll文件和exe文件的区别和关系

dll文件 DLL(Dynamic Link Library)文件为动态链接库文件&#xff0c;又称"应用程序拓展"&#xff0c;是软件文件类型。在Windows中&#xff0c;许多应用程序并不是一个完整的可执行文件&#xff0c;它们被分割成一些相对独立的动态链接库&#xff0c;即DLL文件&…