この記事は、Manniu による一連のシェーダー チュートリアル ( http://www.unitytrain.cn/course/96 ) を学習した後の簡単な要約です。個人的には、この一連のチュートリアルは高度なシェーダー プログラミングを目的としていないように感じます。むしろ、人間に釣りを教えるという目的に近い。以下の3回に分けて、Shaderの簡単な紹介とグラフィックスの基礎、そして総括ともいえるShaderの関連内容を紹介するCg入門編とさせていただきます。
1: シェーダの簡単な説明
a. まず GPU と CPU の違いについて説明しますが、簡単に言うと、GPU は主に表示に関するデータ処理を担当し、CPU は主にオペレーティング システムとアプリケーションを担当します。ディスプレイ関連のデータを CPU に直接渡して処理してはどうでしょうか? 以下に説明を添付します。
b. シェーダの分類。Shader は中国語で「シェーダー」と訳され、プログラマブル グラフィックス パイプラインを意味します。主に、頂点シェーダーとフラグメント シェーダー、つまり固定小数点シェーダーとフラグメント シェーダーに分かれています。上記の概念の 1 つは「グラフィックス パイプライン」です。簡単に説明すると、グラフィックス ディスプレイを処理するコンピュータの処理パイプラインです。
c.Shader の主流のプログラミング言語。主流のシェーダー プログラミング言語には、主に HLSL、GLSL、CG が含まれます。違いについて簡単に説明します。HLSL (High Level Shader Language) は Microsoft の DX ベースの作品であり、Windows プラットフォームでのみ実行できます。OpenGL シェーディング言語である GLSL (OpenGL Shading Language) は、 OpenGLでのシェーディング プログラミングに使用される言語です(OpenGL は、クロスプログラミング言語とクロスプラットフォームプログラミング インターフェイス仕様を定義するプロフェッショナルグラフィックスプログラム インターフェイスです)。シェーディングマシン言語。この時点で、基礎となるグラフィックス ドライバーが上位レベルのプログラミング言語を制限しているという、さらに厄介な問題が発生していることがすでにわかります。グラフィックス ドライバー ライブラリを変更したい場合は、シェーダー ファイル全体を書き直す必要があります。現時点では、CG 時代の要求に応じて出現し、CG は HLSL および GLSL 上でさらにカプセル化され、上位層のシェーダ言語の基礎となるグラフィックス ライブラリへの依存性が保護されています。
d.Unityシェーダー。ShaderLab は実際には Unity のシェーダー構文構造のパッケージであり、サーフェス シェーダー、頂点およびフラグメント シェーダー、および固定関数シェーダーの 3 種類のシェーダーをサポートします。固定機能シェーダは比較的「保守的」なシェーダ(互換性が最も高い) 頂点シェーダとフラグメントシェーダはHLSL、GLSL、またはCG言語領域でのみ記述可能 サーフェスシェーダは頂点とフラグメントの構文ラッパーであり、最終的には翻訳される頂点シェーダーとフラグメントシェーダーで。(上記の詳細情報については、公式ドキュメントhttp://docs.unity3d.com/Manual/index.htmlを参照してください)
2: グラフィックスの基礎
個人的には、このセクションの内容は、Untiy の座標変換とレンダリング プロセスに関するこれまでの盲点を解消する、グラフィックスに慣れていない人にとって非常に価値のある内容であると感じています。以下では、この部分の内容を 2 つのサブセクションに分けて説明します。
a.3D 数学的基礎。実際のところ、3 次元数学は行列に関する演算にすぎず、線生成を学習した学生にとってはまったく問題ありませんので、ここで簡単に紹介します。
1. 座標系とベクトル。3D は左手座標系と右手座標系に分かれており、( http://www.cnblogs.com/mythou/p/3327046.html ) を参照すると、概略図は次のようになります。
2. ベクトル関連の詳細については説明しません。より重要なのはベクトルの内積と外積です。参考記事は次のとおりです ( http://blog.csdn.net/augusdi/article/details/20037851)。
3. マトリックス相関。3D 数学では、行列は変換を表すことが多く、これは座標系変換が依存する数学的原理でもあります。Unity の「MVP マトリックス」については誰もが聞いたことがあるはずですが、MVP マトリックスとは、実際には行列演算を通じて座標系を変換する方法です。Unity には、モデル座標系、ワールド座標系、カメラ座標系、スクリーン座標系の 3 種類の座標系があります。これは実際には 3D 画像表示のためのプロセスです。_ObjectToWorld (つまり M 行列) を使用してモデル自身の座標をワールド座標系に変換し、次に _WorldToCamera (つまり V 行列) を使用してワールド座標系からカメラ座標系に変換します。最後に、_ Projection (P マトリックス) を使用して、カメラからスクリーン座標系への変換を実現し、最終的にスクリーン上に 3D 画像を表示します。Baidu Wenku の記事を添付します (http://wenku.baidu.com/link) ?url=A3AGV805UK5rcsEjkaL1h6QjnxsktvCscyNJqaHvfe2cIhwXMam6ZzH4Gxbu_XB7Jd7ripxjd0eR51Q6cP t9xPxTiX3MeHtFaWkwexBlZti )。
行列に対する重要な演算には、行列の行列式、行列の転置、行列の四則演算、行列の逆変換などが含まれます。これらの知識についてはここでは説明しません。ここでは、いくつかの一般的な行列変換について簡単に紹介します。
行列を座標軸の周りで回転させる:
スケーリング行列:
射影行列
変換行列
上記は一般的に使用されるいくつかの行列ですが、詳細については、Du Niang と Google に頼る必要があります。
b. 以下では、照明カリング、拡散反射、およびハイライトの実装など、いくつかの単純なグラフィックス アプリケーションを紹介します。
1. 軽い淘汰。このとき、「法線の概念」(ある平面に対して常に垂直な点線)を理解する必要があります。私たちの視点は、物体からカメラへのベクトルです。法線Nと直線のなす角が視線 E が 90 度未満の場合、観察者はほぼ正面にあり、逆に 90 度を超える場合は、表面の反対側にあるはずです。 (法線はベクトル差の乗算を使用して取得でき、角度の計算はベクトル点の乗算を使用して計算できます) この時点で必要なオブジェクトを除外した後、次の 2 つの図で簡単に説明します。
2.漫反射(Diffuse 是投射在几盒体表面上的光向各个方向反射的现象),可以简单理解成光照对物体表面颜色的影响(在Unity中默认的Shader其实就是漫反射加环境光的综合作用)。那么该怎样计算光照对物体颜色的影响程度呢?此时还是需要用到法线,我们使用法线和光向量(必须先标准化)的点乘作为影响该区域颜色的因子,这样再乘以该光源的颜色信息就可以得到对应受光照影响后的颜色了,下面用简图说明一下:
3.高光(Specular 光源照射到物体然后反射到人的眼睛里时,物体上最亮的那个点就是高光),从定义就可以得出高光其实和反射光与视角相互作用形成的,同样的我们在计算高光也是利用同样的原理:由入射光求反射光、再计算反射光和视向量的点乘得出影响因子,最后算出高光强度,简图说明一下:
以上是三种比较常见的光照相关的知识,更多资料只能依靠度娘了……
三、最后一部分的内容就简单介绍一下Unity Shader 的语法基础和一个Demo,更具体的还是要参考Unity官方文档。
a.ShaderLab 语法基础。Unity 其实是支持上述三种Shader的,此处介绍的是Vertex and fragment Shader ,用的是CG语法。下面先贴一段Untiy 默认的
//Shader 文件在选择面板以树状结构组织的 Shader "Hidden/NewImageEffectShader" { //这个申明程序中所需要的变量信息 Properties { //_MainTex 变量名 ; “Texture” 在Inspector面板上显示的名称 ; 2D 指变量类型 // "white" 变量默认值 _MainTex ("Texture", 2D) = "white" {} } // Shader 语法块,一个Shader程序至少有一个SubShader,系统在渲染时会依次调用, // 直到找到匹配的SubShader,否则使用最后默认指定的Shader SubShader { // Cull Off:关闭阴影剔除 、 ZWrite : 要将像素的深度写入深度缓存中 // Test Always:将当前深度值写到颜色缓冲中 Cull Off ZWrite Off ZTest Always //渲染通道,固定写法 Pass { //Shader 代码段开始 CGPROGRAM //指定顶点Shader入口 #pragma vertex vert //指定片段程序入口 #pragma fragment frag //引用Unity内置的一些定义 #include "UnityCG.cginc" //自定义结构体 struct appdata { //float4 4维向量、POSITION 语义,相当于告诉渲染引擎,这个变量是代表什么含义 float4 vertex : POSITION; //TEXCOORD0 纹理语义 float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; //Vertex Shader 对应的入口 v2f vert (appdata v) //appdata v 作为参数,渲染引擎会把对应语义的信息传递进来,此处会传递顶点的位置信息和纹理信息 { v2f o; //传递进来的顶点坐标是模型坐标系中的坐标值,需要经过矩阵转换车成屏幕坐标 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.uv; //将计算后的结果输出给渲染引擎,底层会根据具体的语义去做对应的处理 return o; } //在Properties 中定义的变量需要在此申明一下才能在程序中使用 sampler2D _MainTex; //fragment Shader 对应的入口 fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); // just invert the colors col = 1 - col; return col; } ENDCG } } //当上述的SubShader无法匹配硬件环境时,会调这个指定的默认Shader Fallback "Mobile/VertexLit" }
以上就是对Unity中的Vertex and fragment 中使用CG 语法的简单叙述,下面贴上一个Demo
二:Shader Demo,这里贴上一个简单的Demo,Demo的整个是一个Plane,没有使用任何的贴图,仅仅是使用Shader 改变其顶点和颜色信息实现的。下面是Demo的截图
下面贴上该Shader 的源码
Shader "Cus/Demo_3" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { // No culling or depth Cull Off ZWrite Off ZTest Always Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" //自定义结构体,包含位置和颜色语义 struct v2f { float4 pos : POSITION; float4 col : COLOR; }; //Vertex shader入口,颜色信息也在此一并处理了 v2f vert (appdata_base v) { v2f o; //计算旋转角度,利用_SinTime.w为旋转角度加上周期变换性质(_SinTime 是Unity提供的内置变量) float angle = length(v.vertex)* _SinTime.w; //绕Y轴旋转矩阵 float4x4 RM={ float4(cos(angle) , 0 , sin(angle) , 0), float4(0 , 1 ,0 , 0), float4(-1 * sin(angle) , 0 , cos(angle),0), float4(0 , 0 ,0 ,1) }; //利用RM矩阵影响顶点位置信息 float4 pos = mul(RM , v.vertex); //把顶点信息转换到世界坐标系中 o.pos = mul(UNITY_MATRIX_MVP, pos); //由顶点到中心点的距离决定颜色信息 angle = abs(sin(length(v.vertex))); o.col = float4(angle , 1 , 0 ,1 ); return o; } //フラグメントプログラムでは頂点シェーダで計算したカラー情報を直接返す float4 frag (v2f v) : color { return v.col ; } ENDCG } } }
OK、この共有はここで終了します。以下に 3D 画像レンダリング プロセスをシミュレートするために C# で書かれたデモと、上記のシェーダーのデモを添付します (リンク: http://pan.baidu.com/s/1c0Yk3KG パスワード: 1j1k )