Unity+Shader入门精要-1. 入门shader

2024-04-30 09:52
文章标签 入门 unity shader 精要

本文主要是介绍Unity+Shader入门精要-1. 入门shader,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天开始正式整合学习的shader内容。

Simple Shader

主要介绍了大概的shader格式。

Shader "Unity Sgaders Book/Chapter 5/Simple Shader" //shader名
{Properties{//声明color类型的属性_Color("Color Tint", Color) = (1.0,1.0,1.0,1.0)}SubShader{Pass{Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert //顶点着色器函数#pragma fragment frag //片元着色器函数fixed4 _Color; //定义color类型//模型空间的输入顶点信息struct a2v {float4 vertex:POSITION; //模型空间的顶点坐标float3 normal:NORMAL;//模型空间的法线方向float4 texcoord:TEXCOORD0;//模型的第一套纹理坐标};//齐次裁剪空间的输出顶点信息struct v2f {float4 pos : SV_POSITION;//裁剪空间的顶点坐标fixed3 color : COLOR0;//颜色信息};//输入模型空间的顶点信息,经过顶点着色器函数,输出齐次裁剪空间的顶点信息v2f vert(a2v v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);return o;}//输入齐次裁剪空间的顶点信息(经过插值之后),输出顶点颜色信息fixed4 frag(v2f i) : SV_Target{fixed3 c = i.color;c *= _Color.rgb;return fixed4(c,1.0);}ENDCG}}
}

其主要意义在于通过模型法线获取不同参数,从而在材质面板中(材质面板默认显示球体,球体上每一个顶点的法线都不一样)显示一个颜色拾取器。效果如下图所示:

False Color

假彩色图像(false-color image)用于可视化一些数据,以方便对shader进行调试。即将想要的数据映射到[0-1]区间,作为颜色输出到屏幕上,然后通过屏幕上显示的像素颜色来判断这个值是否正确。通常用于debug。shader代码如下:

Shader "Unity Sgaders Book/Chapter 5/False Color"
{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct v2f{float4 pos:SV_POSITION;fixed4 color : COLOR0;};v2f vert(appdata_full v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);//可视化法线方向o.color = fixed4(v.normal * 0.5 + fixed3(0.5,0.5,0.5),1);//可视化切线方向//o.color = fixed4(v.tangent * 0.5 + fixed3(0.5,0.5,0.5),1);//可视化第一组纹理坐标//o.color = fixed4(v.texcoord.xy, 0, 1);//可视化顶点颜色//o.color = v.color;return o;}//不知道作者为何在这里只输出一个fixed参数,即只利用了第一个颜色参数,输出出来的只有红色fixed frag(v2f i) :SV_Target{return i.color;}ENDCG}}
}

比如输出法线可视化,效果如下:

Diffuse Vertex Level/Diffuse Pixel Level

漫反射光照即是入射光线经法线后的反射光线的强度与“入射光线和发现之间的点积”有正比关系。颜色方面,需要叠加材质的漫反射颜色和入射光线的颜色。于是得出以下公式:

可以写下如下代码(逐顶点光照):

Shader "Unity Sgaders Book/Chapter 5/Diffuse Vertex Level"
{Properties{//声明color类型的属性_Diffuse("Diffuse", Color) = (1,1,1,1)}SubShader{Pass{Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Diffuse;struct a2v{float4 vertex:POSITION;float3 normal:NORMAL;};struct v2f{float4 pos:SV_POSITION;fixed3 color : COLOR;};v2f vert(a2v v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);//模型空间坐标转换为齐次裁剪空间fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//环境光照信息fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));//转换法线从模型空间转为世界空间fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);//标准化世界光照向量信息//光照公式,等于自发光+漫反射+环境光//这一步求漫反射fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));//这一步求漫反射和环境光交互o.color = ambient + diffuse;return o;}fixed4 frag(v2f i) :SV_Target{return fixed4(i.color,1);}ENDCG}}
}

其中Diffuse为可调整的漫反射颜色。除了本身的漫反射计算以外,还需要考虑环境光的交互,所以需要加上环境光的颜色。

效果如下:

可以看到逐顶点光照的缺点就是有很明显的锯齿状,对于细分程度较低的模型会遇到。因此可以采用逐像素光照,即把color赋值的操作转移到frag函数中实现,在顶点着色器部分只处理和顶点有关的数据转换即可:

Shader "Unity Sgaders Book/Chapter 5/Diffuse Pixel Level"
{Properties{//声明color类型的属性_Diffuse("Diffuse", Color) = (1,1,1,1)}SubShader{Pass{Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Diffuse;struct a2v{float4 vertex:POSITION;float3 normal:NORMAL;};struct v2f{float4 pos:SV_POSITION;float3 worldNormal:TEXCOORD0;};v2f vert(a2v v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);//模型空间坐标转换为齐次裁剪空间o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//转换法线从模型空间转为世界空间return o;}fixed4 frag(v2f i) :SV_Target{fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//环境光照信息fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);//标准化世界光照向量信息//光照公式,等于自发光+漫反射+环境光//这一步求漫反射fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(i.worldNormal, worldLight));//这一步求漫反射和环境光交互fixed3 color = ambient + diffuse;return fixed4(color,1);}ENDCG}}
}

效果如下:

可以看到光滑了很多。

Half Lambert

以上介绍的是兰伯特光照模型,使用max(0,dot(n,I))来保证点积为非负数。我们也同样可以做α倍的缩放和β的偏移,来让dot(n,I)从[-1,1]的范围映射到[0,1]的范围,如下公式:

绝大部分情况,α=β=0.5。

因此稍微修改之前的逐像素光照模型即可完成任务:

Shader "Unity Sgaders Book/Chapter 5/Half Lambert"
{Properties{//声明color类型的属性_Diffuse("Diffuse", Color) = (1,1,1,1)}SubShader{Pass{Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Diffuse;struct a2v{float4 vertex:POSITION;float3 normal:NORMAL;};struct v2f{float4 pos:SV_POSITION;float3 worldNormal:TEXCOORD0;};v2f vert(a2v v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);//模型空间坐标转换为齐次裁剪空间o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//转换法线从模型空间转为世界空间return o;}fixed4 frag(v2f i) :SV_Target{fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//环境光照信息fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);//标准化世界光照向量信息//光照公式,等于自发光+漫反射+环境光//这一步求漫反射fixed halfLambert = saturate(dot(i.worldNormal, worldLight)) * 0.5 + 0.5;fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;//这一步求漫反射和环境光交互fixed3 color = ambient + diffuse;return fixed4(color,1);}ENDCG}}
}

效果如下:

显而易见地观察到,它会比之前的光照模型要亮,是因为几乎没有diffuse=0的情况,几乎所有点都是亮的。

Specular Vertex Level/Specular Pixel Level

除了漫反射,还需要考虑高光反射,高光反射的计算公式如下:

即入射光的颜色和强度作用于带有高光属性的材质上,其与“视角方向和反射方向的点积”成正比关系。

 因此延续漫反射光照模型继续写下去,只是添加了高光数据,如下逐顶点光照:

Shader "Unity Sgaders Book/Chapter 5/Specular Vertex Level"
{Properties{_Diffuse("Diffuse", Color) = (1,1,1,1) //声明color类型的属性_Specular("Specular",Color) = (1,1,1,1) //高光反射颜色_Gloss("Gloss",Range(8,256)) = 20 //高光区域大小}SubShader{Pass{......fixed4 _Diffuse;fixed4 _Specular;float _Gloss;......v2f vert(a2v v){......//光照反射fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal));//计算入射光关于法线的反射方向fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);//视角方向fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);//这一步求漫反射和环境光和高光o.color = ambient + diffuse + specular;return o;}......}}
}

同样有锯齿问题,于是引入逐像素光照,和diffuse pixel level类似,只需要在顶点着色器内部处理顶点相关信息,在片元着色器内部处理颜色信息即可:

Shader "Unity Sgaders Book/Chapter 5/Specular Pixel Level"
{Properties{_Diffuse("Diffuse", Color) = (1,1,1,1) //声明color类型的属性_Specular("Specular",Color) = (1,1,1,1) //高光反射颜色_Gloss("Gloss",Range(8,256)) = 20 //高光区域大小}SubShader{Pass{......fixed4 _Diffuse;fixed4 _Specular;float _Gloss;......fixed4 frag(v2f i) :SV_Target{......//光照反射fixed3 reflectDir = normalize(reflect(-worldLightDir, i.worldNormal));//计算入射光关于法线的反射方向fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);//视角方向fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);//这一步求漫反射和环境光和高光fixed3 color = ambient + diffuse + specular;return fixed4(color,1);}ENDCG}}
}

效果如下所示:

BlinnPhong

BlinnPhong光照模型和Phong模型的区别则是:Phong的高光计算是视角与“入射光相对法线反射之后的光”作点积运算,而BlinnPhong的高光计算则是法线与“视角和入射光相加后归一化”的向量作点积运算。

因此只需要修改部分Specular Pixel Level的片元着色器代码即可完成任务:

            fixed4 frag(v2f i) :SV_Target{......//光照反射fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));//视角方向fixed3 halfDir = normalize(viewDir+worldLightDir);fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, i.worldNormal)), _Gloss);//这一步求漫反射和环境光和高光fixed3 color = ambient + diffuse + specular;return fixed4(color,1);}

 相比于Phong模型的高光反射部分看起来会更大更亮一些:

这篇关于Unity+Shader入门精要-1. 入门shader的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

MySQL-CRUD入门1

文章目录 认识配置文件client节点mysql节点mysqld节点 数据的添加(Create)添加一行数据添加多行数据两种添加数据的效率对比 数据的查询(Retrieve)全列查询指定列查询查询中带有表达式关于字面量关于as重命名 临时表引入distinct去重order by 排序关于NULL 认识配置文件 在我们的MySQL服务安装好了之后, 会有一个配置文件, 也就

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C语言指针入门 《C语言非常道》

C语言指针入门 《C语言非常道》 作为一个程序员,我接触 C 语言有十年了。有的朋友让我推荐 C 语言的参考书,我不敢乱推荐,尤其是国内作者写的书,往往七拼八凑,漏洞百出。 但是,李忠老师的《C语言非常道》值得一读。对了,李老师有个官网,网址是: 李忠老师官网 最棒的是,有配套的教学视频,可以试看。 试看点这里 接下来言归正传,讲解指针。以下内容很多都参考了李忠老师的《C语言非

MySQL入门到精通

一、创建数据库 CREATE DATABASE 数据库名称; 如果数据库存在,则会提示报错。 二、选择数据库 USE 数据库名称; 三、创建数据表 CREATE TABLE 数据表名称; 四、MySQL数据类型 MySQL支持多种类型,大致可以分为三类:数值、日期/时间和字符串类型 4.1 数值类型 数值类型 类型大小用途INT4Bytes整数值FLOAT4By

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是