Shader编程学习笔记(十二)—— Cg语言入门4 -函数

18 篇文章 1 订阅
16 篇文章 1 订阅


1、引言

  这一篇我们俩了解下Cg语言中的函数,这里涉及到的知识点包括以下内容:

  • 定制函数
  • 函数的前项声明
  • 函数的传参
  • Cg include
  • Cg中的内建函数

2、示例

  这我们将一一举例说明!开始之前我们先搭建好场景,便于演示。创建一个场景,一个cube,一个材质球和一个顶点片元shader。然后该cube使用新建的材质,选择刚才闯将的shader,修改代码如下:

Shader "lxt610/NewSurfaceShaderTest3" {
    SubShader 
	{
		Pass
		{
			CGPROGRAM
        
	#pragma fragment frag
	#pragma vertex vert
		
			void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
			{
				pos = float4(objPos,0,1);			
				
				col = float4(1,0,0,1);//红色

				//col = pos;
			}

			void frag(inout float4 col:COLOR0)
			{				
			}
			ENDCG
		}
    }
}

  运行场景后显示红色。

2.1、函数

  函数的语法格式:

返回值类型 函数名称([参数列表])
{
		//语句块;
}

  下面是一个示例

Shader "lxt610/NewSurfaceShaderTest3" {
    SubShader 
	{
		Pass
		{
			CGPROGRAM
       
	#pragma fragment frag
	#pragma vertex vert
			void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
			{
				pos = float4(objPos,0,1);						
				col = float4(1,0,0,1);//红色
			}

			void frag(inout float4 col:COLOR0)
			{				
				 func();
			}
				
			void func()
			{			
			}
			
			ENDCG
		}
    }
}

  写完回到unity,编译完毕发现异常了,提示函数未定义!Cg中需要把声明的函数放在使用的位置之前,否则就异常了。这时只需要改为:

void func()
{			
}
void frag(inout float4 col:COLOR0)
{				
	 func();
}		

  就可以了!然而有时候我们就想在后面声明怎么办?

2.2、函数的前项声明

  此时可以考虑函数的前项声明。使用方式就是:在被调用的函数的位置前面声明一个前置函数,其函数名返回值类型及参数列表和声明的函数相同就可以了。eg:

Shader "lxt610/NewSurfaceShaderTest3" {
    SubShader 
	{
		Pass
		{
			CGPROGRAM
       
	#pragma fragment frag
	#pragma vertex vert
			void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
			{
				pos = float4(objPos,0,1);						
				col = float4(1,0,0,1);//红色
			}
			void func();
			void frag(inout float4 col:COLOR0)
			{				
				 func();
			}
				
			void func()
			{		
			}
		
			ENDCG
		}
    }
}

  此时我们发现编译通过!

2.3、函数的参数传递

  我们再次修改代码

Shader "lxt610/NewSurfaceShaderTest3" {
    SubShader 
	{
		Pass
		{
			CGPROGRAM
       
	#pragma fragment frag
	#pragma vertex vert
			void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
			{
				pos = float4(objPos,0,1);						
				col = pos;
			}
			void func(float4 col);
			void frag(inout float4 col:COLOR0)
			{				
				func(col);
			}
				
			void func(float4 col)
			{
				 col = float4(0,1,0,1);
			}			
			ENDCG
		}
    }
}

  这里我们发现颜色没有变化为想要的颜色,这时由于Cg中这里的值进行了值Copy,并不是操作的原来的值!要想改变可以使用out关键字:

Shader "lxt610/NewSurfaceShaderTest3" {
    SubShader 
	{
		Pass
		{
			CGPROGRAM
       
	#pragma fragment frag
	#pragma vertex vert
			void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
			{
				pos = float4(objPos,0,1);						
				col = pos;
			}
			void func(out float4 col);
			void frag(inout float4 col:COLOR0)
			{				
				func(col);
			}
				
			void func(out float4 col)
			{
				 col = float4(0,1,0,1);
			}			
			ENDCG
		}
    }
}

  编译通过后我们发现颜色变为了绿色。我们可以知道这里的out参数只是把数值传递进来,把值做了改变,并传回去了。这个out类似于C#中的ref关键字。当我们不需要Copy一个变量,需要这个值得时候可以使用out参数
  当我们需要传递一个数组时,不可以像其他语言一样,需要这样写:

float func2(float arr[3])
{
	float sum = 0;
	for(int i = 0; i < arr.Length; i++)
	{
		sum += arr[i];
	}

	return sum;
}

void frag(inout float4 col:COLOR0)
{				
	//func(col)
	float arr[2] = {0.5,0.5};
	col.x = func2(arr);
}

这里需要注意的是:

  • 声明的函数的参数列表中如果是数组时,我们需要指定这个函数的长度,我们同时在调用的时候也必须传递给这个函数相同长度的数组
  • 同时声明数组时如果我们是ES3.0需要指定数组的长度,eg:float arr[2] = {0.5,0.5};
  • 我们注意到Cg中数组有一个关键字Length,用来表示数组的长度。

最终的代码和效果如下:

Shader "lxt610/NewSurfaceShaderTest3" {
    SubShader 
	{
		Pass
		{
			CGPROGRAM
			
#pragma fragment frag
#pragma vertex vert

			void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
			{
				pos = float4(objPos,0,1);						
				col = pos;
			}

			void func(out float4 col);
			float func2(float arr[2])
			{
				float sum = 0;
				for(int i = 0; i < arr.Length; i++)
				{
					sum += arr[i];
				}

				return sum;
			}

			void frag(inout float4 col:COLOR0)
			{				
				//func(col)
				float arr[2] = {0.5,0.5};
				col.x = func2(arr);
			}
				
			void func(out float4 col)
			{
				 col = float4(0,1,0,1);
			}

			ENDCG
		}
    }
}

在这里插入图片描述
  同样这里的数组也是一个值Copye。因此在Cg中参数的传递是通过值Copy的形式来传递的,需要使用这个参数时可以使用out关键字。

2.4、Cg include

  当我们写了很多shader之后,我们发现不同的shader在使用同样的函数,有没有什么办法解决这样的问题呢?这里就需要使用Cg当中的include指令。此时我们不妨打开Unity\Unity2017.4.27f1\Editor\Data\CGIncludes目录,可以看到有很多.cginc文件,我们打开UnityCG.cginc,我们发现有很多函数和数据类型,我们可以使用include把这个文件包含进来,就可以使用这里面的函数和数据类型了。
  那么当我们自己写shader的时候可不可以使用呢?当然也是可以的了。这里我们创建一个.cginc文件命名为lmy225.cginc,用来存储自己的一些函数。为了方便查找同时不和官方的cginc混淆这里我专门创建了一个名为lmy225的文件夹用来存储自己的cginc文件,此时把lmy225.cginc放在这个文件夹中,这里我们可以把:

float func2(float arr[2])
{
	float2 sum = 0;
	for(int  i = 0; i < arr.Length;i++)
	{
		sum += arr[i];
	}
	return sum;
}

void func(out float4 c)
{
	c = float4(0,1,0,1);
}

放在cginc中,然后回到shader删除前项声明,使用下面的include指令:

#include "路径"

eg:

Shader "lxt610/NewSurfaceShaderTest3" {
    SubShader 
	{
		Pass
		{
			CGPROGRAM
			
#pragma fragment frag
#pragma vertex vert

			#include "lmy225/lmy225.cginc"

			void vert(in float2 objPos:POSITION,out float4 col:COLOR0,out float4 pos:POSITION)
			{
				pos = float4(objPos,0,1);						
				col = pos;
			}

			void frag(inout float4 col:COLOR0)
			{				
				//func(col)
				float arr[2] = {0.5,0.5};
				col.x = func2(arr);
			}

			ENDCG
		}
    }
}

  编译运行后我们发现实现了相同的效果!

这里include指令后面的路径可以使用\或/,也可以混合使用!
当然这里的.cginc后缀也可以是别的自己喜欢的后缀。只是改为其他后缀之后,关键字没有高亮提示了。

2.5、Cg中的内建函数

  在cg的内建函数中主要有以下四类:

  • 数学函数
  • 几何函数
  • 纹理函数
  • 导数函数
      这里我们给出Cg标准函数的文档,需要的同学,可以点击这里下载。当然除了上面的还有debug函数,由于功能比较单一,只是把数值通过颜色语义来显示出来!所以很少使用!

3、结束语


The End
  好了,今天的分享就到这里,如有不足之处,还望大家及时指正,随时欢迎探讨交流!!!


喜欢的朋友们,请帮顶、点赞、评论!您的肯定是我写作的不竭动力!

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

对酒当歌﹏✍

您的鼓励是我写作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值