Unity SRP自定义渲染管线学习2.1: DrawCalls 编写无光物体Shader

本文主要是介绍Unity SRP自定义渲染管线学习2.1: DrawCalls 编写无光物体Shader,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

写在前面

要绘制出物体,CPU必须告诉GPU绘制什么,怎么绘制?通常Mesh控制了绘制什么,Shader则控制怎么绘制,会给GPU一系列的设置。除了Mesh外,Shader需要额外的信息,包括物体的空间信息矩阵,材质属性等。

Unity的通用渲染管线和高清渲染管线中,可以通过Shader Graph插件来编辑Shader,但是如果是我们自己定义的渲染管线,我们必须自己写Shader代码,自己写的话,我们能够完全控制并且理解好Shader所要做的工作。

软件环境

我使用的Unity版本:Unity2020.3.10f1

编写Shader

我们创建第一个无光只有纯色的Shader,创建后先清空所有的内容
在这里插入图片描述
要绘制一个Mesh,GPU必须将Mesh的面进行光栅化,转换成像素数据,通过将顶点坐标从3维空间转换到2维的视图空间,再填入像素数据。这两步就是通过我们自己定义的顶点函数(vertex kernel/program/shader),片元函数(fragment kernel/program/shader)。一个片元对应着显示一个像素或者是纹理像素,但可能不是最终显示的,因为它的上面还可能有其他物体会把它给覆盖掉。
我们先在Unlit Shader中写好基本结构


Shader "Custom RP/Unlit"
{Properties {}SubShader {Pass {HLSLPROGRAM  //CGPROGRAM 和 HLSLPROGRAM 都是支持的,但我们看现在的Unity官方的渲染管线都是这么写的了//pragma来源于希腊语,指需要做某些事或者一个操作,在很多的编程语言中都用于表示特殊的编译器指令,对,就是指令,或许中文的“指令”一词能比较好的代表其的含义#pragma vertex UnlitPassVertex#pragma fragment UnlitPassFragment#include "UnlitPass.hlsl"  //对应着.cginc,和上面的HLSLPROGRAM一个道理ENDHLSL}}
}

我们把一些方法放在UnlitPass.hlsl中,和之前的.cginc是一个道理
在这里插入图片描述
先简单实现我们需要的顶点、片元函数


#ifndef CUSTOM_UNLIT_PASS_INCLUDED  //避免被多个文件include,导致重复定义,导致编译错误,基本上头文件开头都得这样子写
#define CUSTOM_UNLIT_PASS_INCLUDED/* 输入的坐标为什么定义为float3,而不是float4
点坐标和方向向量都被定义为4维坐标,点坐标的w=1,方向向量的w=0,这是为了能够直接对坐标和方向进行矩阵变换。
所以我们定义坐标和方向向量时,如果不需要混合运算的话,我们可以先只定义3维向量,比如把输入数据定义为float3,在最后加上1或0即可,这样可以节约开销。
*/
float4 UnlitPassVertex(float3 positionOS : POSITION) : SV_POSITION  //语义SV_POSITION指返回值是位置坐标,语义需要指出返回的值的用途
{//positionOS是物体空间坐标,Object Spacereturn float4(positionOS, 1.0);// return 0.0;  
}
/*  关于Shader值类型使用float、half、fixed的纠结说明
大部分的移动设备的GPU支持两种精度类型:float和half,half类型性能更优,所以如果在写移动端的shader时可以尽可能的使用half。
根据经验,仅仅位置、纹理坐标需要使用float类型,其他的都可以使用half类型,结果相差无几
但如果不是移动设备,使用哪种精度类型就无所谓了,因为即使我们使用half,实际上GPU用的也是float
而对于fixed类型,只有在一些老的移动设备中才会支持,现在的设备基本都不再支持,所以即使写的是fixed,实际上也是用half
*/
float4 UnlitPassFragment() : SV_TARGET  //语义SV_TARGET指返回值是用于RenderTarget的
{return 0.0;  //返回一个值会直接被转换成(0.0, 0.0, 0.0, 0.0),因为是不透明物体,所以alpha通道为0也没关系
}
#endif

我们先尝试了一下在顶点函数中直接返回物体空间坐标,错误表现
在这里插入图片描述
仿照Unity官方管线的做法,添加一个ShaderLibrary目录,添加一个UnityInput文件,用来存放一些通用的方法,比如马上要写的转换空间坐标的方法
在这里插入图片描述


#ifndef CUSTOM_UNITY_INPUT_INCLUDED
#define CUSTOM_UNITY_INPUT_INCLUDED
float4x4 unity_ObjectToWorld;  //每一次绘制GPU设置这个值,然后在一次绘制中的顶点片元函数使用期间值不变
float4x4 unity_MatrixVP;  //View-Projection Matrix#endif

Common.hlsl
在这里插入图片描述

#ifndef CUSTOM_COMMON_INCLUDED
#define CUSTOM_COMMON_INCLUDED
#include "UnityInput.hlsl"
float3 TransformObjectToWorld(float3 positionOS)
{//Swizzle operation: In computer graphics, swizzling is the ability to compose vectors by arbitrarily rearranging and combining components of other vectors.[1] For example, if A = {1,2,3,4}, where the components are x, y, z, and w respectively, you could compute B = A.wwxy, whereupon B would equal {4,4,1,2}.return mul(unity_ObjectToWorld, float4(positionOS, 1.0)).xyz;// return 0.0;
}float4 TransformWorldToHClip(float3 positionWS)
{return mul(unity_MatrixVP, float4(positionWS, 1.0));
}#endif

然后我们就可以在顶点片元函数中使用这些方法了
UnlitPass.hlsl


float4 UnlitPassVertex(float3 positionOS : POSITION) : SV_POSITION  //语义SV_POSITION指返回值是位置坐标,语义需要指出返回的值的用途
{//positionOS是物体空间坐标,Object Spacefloat3 positionWS = TransformObjectToWorld(positionOS.xyz);return TransformWorldToHClip(positionWS);// return float4(positionWS, 1.0);// return 0.0;  
}

当我们在顶点函数最后输出的是裁剪空间坐标后大小就正确了
在这里插入图片描述

Swizzle operation

什么是Swizzle operation或者Swizzling,就是通过任意组合排列一个向量的元素得到另外一个向量的操作,比如A = {1, 2, 3, 4},我们让B = A.wxxx,那么 B = {4, 1, 1, 1},这个操作就叫Swizzle operation或者Swizzling

Wiki上原文:
In computer graphics, swizzling is the ability to compose vectors by arbitrarily rearranging and combining components of other vectors.[1] For example, if A = {1,2,3,4}, where the components are x, y, z, and w respectively, you could compute B = A.wwxy, whereupon B would equal {4,4,1,2}.

猫神原文:
在这里插入图片描述

使用Core RP Library

实际上我们还可以直接使用Core RP Library,其中有很多已经定义好的通用方法
在这里插入图片描述
UnlitPass.hlsl


#ifndef CUSTOM_UNLIT_PASS_INCLUDED  //避免被多个文件include,导致重复定义,导致编译错误,基本上头文件开头都得这样子写
#define CUSTOM_UNLIT_PASS_INCLUDED
#include "../ShaderLibrary/Common.hlsl" //不是一个目录的,需要使用相对路径

Common.hlsl


#ifndef CUSTOM_COMMON_INCLUDED
#define CUSTOM_COMMON_INCLUDED
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "UnityInput.hlsl"
#define UNITY_MATRIX_M unity_ObjectToWorld  //所有的UNITY_MATRIX_M会被替换成unity_ObjectToWorld,Unity的Package中用的是UNITY_MATRIX_M
#define UNITY_MATRIX_I_M unity_WorldToObject
#define UNITY_MATRIX_V unity_MatrixV
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_MATRIX_P glstate_matrix_projection
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"  //直接将Package的文件包含进来
/* 这两个方法实际上在Core RP Library中已经有定义了,我们可以直接使用Package
float3 TransformObjectToWorld(float3 positionOS)
{//Swizzle operation: In computer graphics, swizzling is the ability to compose vectors by arbitrarily rearranging and combining components of other vectors.[1] For example, if A = {1,2,3,4}, where the components are x, y, z, and w respectively, you could compute B = A.wwxy, whereupon B would equal {4,4,1,2}.return mul(unity_ObjectToWorld, float4(positionOS, 1.0)).xyz;// return 0.0;
}
float4 TransformWorldToHClip(float3 positionWS)
{return mul(unity_MatrixVP, float4(positionWS, 1.0));
}
*/
#endif

UnityInput.hlsl


#ifndef CUSTOM_UNITY_INPUT_INCLUDED
#define CUSTOM_UNITY_INPUT_INCLUDED
float4x4 unity_ObjectToWorld;  //每一次绘制GPU设置这个值,然后在一次绘制中的顶点片元函数使用期间值不变
float4x4 unity_WorldToObject;
real4 unity_WorldTransformParams;
float4x4 unity_MatrixVP;  //View-Projection Matrix
float4x4 unity_MatrixV;
float4x4 glstate_matrix_projection;
#endif

添加颜色属性

我们添加颜色的属性变量
Unlit.shader


Properties {_BaseColor("Color", Color) = (1.0, 1.0, 1.0, 1.0)}

UnlitPass.hlsl


float4 _BaseColor;  //用于Shader中定义颜色属性,名称需相同float4 UnlitPassFragment() : SV_TARGET  //语义SV_TARGET指返回值是用于RenderTarget的
{return _BaseColor;// return float4(1.0, 1.0, 0.0, 1.0);// return 0.0;  //返回一个值会直接被转换成(0.0, 0.0, 0.0, 0.0),因为是不透明物体,所以alpha通道为0也没关系
}

这样我们就可以调整颜色了
在这里插入图片描述

参考

本文主要学习自: https://catlikecoding.com/unity/tutorials/custom-srp/draw-calls/
https://bitbucket.org/catlikecodingunitytutorials/custom-srp-02-draw-calls/src/master/
catlikecoding是大神的博客,里面有很多教程,膜拜大神,感恩大神。

这篇关于Unity SRP自定义渲染管线学习2.1: DrawCalls 编写无光物体Shader的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...

详解如何在React中执行条件渲染

《详解如何在React中执行条件渲染》在现代Web开发中,React作为一种流行的JavaScript库,为开发者提供了一种高效构建用户界面的方式,条件渲染是React中的一个关键概念,本文将深入探讨... 目录引言什么是条件渲染?基础示例使用逻辑与运算符(&&)使用条件语句列表中的条件渲染总结引言在现代

2.1/5.1和7.1声道系统有什么区别? 音频声道的专业知识科普

《2.1/5.1和7.1声道系统有什么区别?音频声道的专业知识科普》当设置环绕声系统时,会遇到2.1、5.1、7.1、7.1.2、9.1等数字,当一遍又一遍地看到它们时,可能想知道它们是什... 想要把智能电视自带的音响升级成专业级的家庭影院系统吗?那么你将面临一个重要的选择——使用 2.1、5.1 还是

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

使用C#如何创建人名或其他物体随机分组

《使用C#如何创建人名或其他物体随机分组》文章描述了一个随机分配人员到多个团队的代码示例,包括将人员列表随机化并根据组数分配到不同组,最后按组号排序显示结果... 目录C#创建人名或其他物体随机分组此示例使用以下代码将人员分配到组代码首先将lstPeople ListBox总结C#创建人名或其他物体随机分组

SpringBoot 自定义消息转换器使用详解

《SpringBoot自定义消息转换器使用详解》本文详细介绍了SpringBoot消息转换器的知识,并通过案例操作演示了如何进行自定义消息转换器的定制开发和使用,感兴趣的朋友一起看看吧... 目录一、前言二、SpringBoot 内容协商介绍2.1 什么是内容协商2.2 内容协商机制深入理解2.2.1 内容

使用Java编写一个文件批量重命名工具

《使用Java编写一个文件批量重命名工具》这篇文章主要为大家详细介绍了如何使用Java编写一个文件批量重命名工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录背景处理1. 文件夹检查与遍历2. 批量重命名3. 输出配置代码片段完整代码背景在开发移动应用时,UI设计通常会提供不

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert