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模型的高光反射部分看起来会更大更亮一些:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

本地生活服务平台哪家强,怎么申请成为服务商?

当下,本地生活服务已经成为了多家互联网大厂布局的重要板块,在巨大的市场需求和强大的资本加持下,不少人都看到了本地生活服务平台广阔的前景和收益空间。在此背景下,许多普通人都跃跃欲试,想要成为本地生活服务商&…

基于RAG的问答机器人

基于RAG的问答机器人 前置条件 什么是RAG https://blog.csdn.net/m0_56699208/article/details/138063866?spm1001.2014.3001.5502 quickstart 构建 概括地说,任何 SQL 链和 agent 的步骤如下: 将问题转换为 SQL 查询:模型将用户输入…

设计模式 策略模式

文章目录 策略模式简介策略模式结构策略模式代码 策略模式简介 策略模式是一种行为型设计模式,它定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。 策略模式结构 策略(Strategy)接口:定义了一个算法族,并声明了…

FebHost:什么是挪威.no域名,如何注册?

挪威国家域名介绍 挪威是一个位于北欧的国家,北面和西面是大西洋和北海,东面和南面则与瑞典、芬兰接壤。挪威是一个高度发达的经济体,其政府在经济管理和可持续发展方面也取得了很多成就。挪威的人均GDP在世界范围内排名非常靠前&#xff0c…

Android 多媒体处理中ByteBuffer使用注意事项

Android多媒体处理中ByteBuffer使用注意事项 ByteBuffer 是 Java 中用来操作原始字节数据的类,它提供了一种灵活的方式来读取、写入和操作字节数据。以下是关于 ByteBuffer 的详细说明: 创建 ByteBuffer 你可以通过几种方式来创建 ByteBuffer&#xf…

笔试刷题-Day10

牛客 一、DP30买卖股票的最好时机(一) 算法:虽然题目标了DP但是用贪心更快页更容易理解 import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Sca…

2024LarkXR新增功能系列之五 | 单端口支持多并发

实时云渲染技术在为虚拟现实、游戏、和各种应用程序提供强大的渲染支持的同时,也带来了一些网络和运维上的挑战。在传统的设置中,实时云渲染推流技术需要为每个视频流单独占用服务器的一个端口。这种方法在多用户同时访问的情况下可能会导致端口资源的快…

QT学习篇—qt软件安装

qt下载网址http://download.qt.io/new_archive/qt/ QT官网Qt | Tools for Each Stage of Software Development LifecycleAll the essential Qt tools for all stages of Software Development Lifecycle: planning, design, development, testing, and deployment.https:…

APP测试面试题汇总

🍅 视频学习:文末有免费的配套视频可观看 🍅 关注公众号【互联网杂货铺】,回复 1 ,免费获取软件测试全套资料,资料在手,涨薪更快 一、基础篇 1、请介绍一下,APP测试流程&#xff1f…

嵌入式学习59-ARM7(自动设备号和混杂设备)

知识零碎: 头文件查找: /arm/路径下的头文件 linux驱动程序的编写,编译,运行过程 -------------------------------------------------------------------------------------------------------------------------------- 1.…

30秒出服装设计稿,森马用Serverless+AIGC 整“新活”!

“创新项目如何去赋能我们的业务,这件事情在森马很重要。阿里云函数计算帮我们屏蔽掉了想把AI落地到实际业务场景中 GPU 算力资源储备、采购成本、技术门槛等很多难题,从而迅速做出决策,快人一步站在正确的起点,体验新技术对整个服…

用友裁应届

下半年准备来用友的24应届生们,请三思!!! 我是23届某9研究生,2月份用友以绩效低为由被裁(我一个应届生跟老员工比绩效,搞笑呢)。 半年被裁,找工作太难了&a…

LangChain之各个输出解析器的使用

Model I/O 在LangChain中,Model I/O被称为:模型的输入与输出,其有输入提示(Format)、调用模型(Predict)、输出解析(Parse)等三部分组成。 makefile 复制代码 1.提示模板: LangChain的模板允许动态选择输入,根据实际需求调整输入内…

【mysql】mysql中的数据类型知多少?

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

WiTUnet:一种集成CNN和Transformer的u型架构,用于改进特征对齐和局部信息融合

WiTUnet:一种集成CNN和Transformer的u型架构,用于改进特征对齐和局部信息融合 摘要IntroductionRelated workMethod WiTUnet: A U-Shaped Architecture Integrating CNN and Transformer for Improved Feature Alignment and Local Information Fusion. 摘要 低剂量…

Python | Leetcode Python题解之第52题N皇后II

题目: 题解: class Solution:def totalNQueens(self, n: int) -> int:def backtrack(row: int) -> int:if row n:return 1else:count 0for i in range(n):if i in columns or row - i in diagonal1 or row i in diagonal2:continuecolumns.add…

反汇编:OllyDBG/objdump分析

一、OllyDBG 1、 2、可以通过file->open的方法打开需要查看的程序 或者是 dll文件 3、反汇编窗口中:各列的含义; 反汇编中的第一列:是指令在内存中存放的地址; 反汇编中的第二列:是所谓的汇编语言中的操作码&…

海洋微生物清净节能剂属于节能环保型产品 我国市场参与者数量不断增长

海洋微生物清净节能剂属于节能环保型产品 我国市场参与者数量不断增长 海洋微生物清净节能剂是一种环保型燃油添加剂,通常以海洋微生物或其代谢产物为基材制成。海洋微生物清净节能剂可以有效降低尾气排放量、改善燃油燃烧效率,在各类燃油设备中拥有广阔…

STM32读写备份寄存器BKP

今天学习的读写STM32的备份寄存器BKP的步骤,这节知识是比较简单的,一共也就两大部: 这个BKP寄存器的意思就是在芯片的VB引脚上接个电池,就能保存其寄存器中的数据掉电不丢失。先来看看电池的接法: 好,下面…

git 清除已提交的记录

git 清除已提交的记录 步骤一 首先确保你本地没有做任何更改 提交你的当前更改: bashCopy codegit add . git commit -m "Committing current changes"执行 rebase 命令: bash Copy code git rebase -i HEAD~2如果你不想保留当前更改&#xf…
最新文章