翻译:Panda3D Manual/V. Programming with Panda/N. Pixel and Vertex Shaders

来源:互联网 发布:重庆php培训 编辑:程序博客网 时间:2024/05/12 11:48
像素着色器与顶点着色器(Pixel and Vertex Shaders
目前,Panda3D只支持Cg着色语言。(赶紧支持HLSL和GLSL吧)
编写Panda3D着色器
这部分假设你对Cg着色器语言有一定了解,否则,你最好先读一读有关Cg的资料。
编写着色器,必须创建一个如下所示的着色器程序。该例子的作用是保留顶点原来的位置,但调换了顶点颜色的红、绿通道。
//Cg
//
//Cg profile arbvp1 arbfp1

void vshader(float4 vtx_position : POSITION,
  float4 vtx_color: COLOR,
  out float4 l_position : POSITION,
  out float4 l_color0 : COLOR0,
  uniform float4x4 mat_modelproj)
{
  l_position=mul(mat_modelproj, vtx_position);
  l_color0=vtx_color;
}

void fshader(
  float4 l_color0 : COLOR0,
  out float4 o_color : COLOR)
{
  o_color=float4(l_color0[1],l_color0[0], l_color0[2], l_color0[3]);
}
着色器的第一行应该为“//Cg”,不要在其中添加任何空格。未来我们将支持别的着色器语言,每一种语言都有自己的文件头标识。
着 色器必须包含两个名为vshader和fshader的子程序,分别为顶点着色器(vertex shader)和片断着色器(fragment shader)。除此以外,还可以包含额外的名为vshader1、 fshader1、 vshader2、 fshader2等等的子程序。后面这些子程序对代表了备选的代码,当显卡不支持前一对着色器时,将使用后面的着色器。如果显卡全都不支持,则该着色器没 有产生作用。(也就是说,渲染过程按常规使用标准的管线(pipeline))。
下面2行代码将载入一个着色器并应用到一个模型上:
myShader = Shader.load("myshader.sha")
myModel.setShader(myShader)
第一行载入着色器,返回一个Shader类对象。调用setShader使myModel用这个着色器来渲染。着色器在scene graph中向下传递:节点以及它的所有子节点都使用该着色器。
着色器可以从Panda Runtime中取得数据
每 个着色器程序包含一个参数列表。Panda3D扫描参数列表,根据参数的名字的意义从panda runtime中准确地抽取数据。例如,如果着色器包含这样一个参数声明float3 vtx_position : POSITION,则Panda3D把它理解为要求得到顶点的位置,并满足该要求。Panda3D只允许它能辨认和理解的参数声明存在。
对 不符合要求的参数,Panda3D将产生错误。例如,如果你声明参数float3 vtx_position,Panda3D将高兴地接受。要是你声明uniform shader2d vtx_position,Panda3D将分别产生2个错误:第一Panda3D认定vtx_position应该是一个浮点向量,而不是一个纹理;第 二它应该是一个可变(varying)参数,而不是一个统一(uniform)参数。
再次强调,所有的参数名字必须能被Panda识别。我们将给出一个着色器输入列表,列出所有合法的参数名以及Panda3D支持的全部数据。
手工为着色器提供数据
通过恰当的参数名,着色器所需的大部分数据都可以从panda runtime系统里取得。但有些时候,必须给着色器提供一些用户数据。为此,我们需要setShaderInput。来看个示例:
myModel.setShaderInput("tint", Vec4(1.0, 0.5, 0.5, 1.0))
setShaderInput方法存储可以被着色器读取的数据,可以存储的数据类型包括Texture、NodePath和Vec4。setShaderInput也接受分离的浮点数,把它们合并成一个Vec4(四元向量)。
你 用setShaderInput存储的数据不是必须被着色器使用。数值存储在节点里,除非着色器明确地要求使用数据,否则它们将保持不动。因此,上面的 myModel.setShaderInput("tint", Vec4(1.0, 0.5, 0.5, 1.0))只是存储了一个名为“tint”的向量,用不用由着色器来决定。
为了取得由setShaderInput提供的数据,着色器必须使用适当的参数名。查看着色器输入列表,可以看到很多数据都是用setShaderInput保存的。
着 色器的输入在scene graph中向下传播,并且积累起来。例如,如果你在一个节点上存储setShaderInput("x",1),在它的子节点上 setShaderInput("y",2),则该子节点同时拥有这两个值。如果如果你在一个节点上存储setShaderInput("z",1),在 它的子节点上setShaderInput("z",2),则后面的值将覆盖掉先前的值。setShaderInput方法接受第三个参数,表示优先级, 默认值为0。如果如果你在一个节点上存储setShaderInput("w",1,1000),在它的子节点上setShaderInput("w", 2,500),则子节点将拥有("w"==1),因为优先级1000高于优先级500。
着色器渲染属性(Shader Render Attributes
nodePath.setShader和nodePath.setShaderInput函数用于一个着色器应用到scene graph上的某个节点。这些函数在节点上操纵ShaderAttrib类的渲染属性。
必须显式操纵ShaderAttrib对象的情况极少出现。下面的例子说明如何创建一个ShaderAttrib并应用到摄影机上:
myShaderAttrib = ShaderAttrib.make()
myShaderAttrib = myShaderAttrib.setShader(Shader.load("myshader.sha"))
myShaderAttrib = myShaderAttrib.setShaderInput("tint", Vec4(1.0,0.5,0.5,1.0))
base.cam.node().setInitialState(render.getState().addAttrib(myShaderAttrib))
注意:attribs是不可变的对象。因此当你对一个ShaderAttrib应用setShader或setShaderInput函数时,并不能改变attrib。这些函数返回一个新的attrib(包含了修改后的数据)。
延后的着色器编译
当你创建了一个shader类对象后,你只是存储了着色器的程序本身。你还没有编译着色器,真正的编译在渲染阶段才进行。
因此,如果着色器含有语法错误,或者你的显卡不支持这个着色器,也只能等到渲染时才能收到出错信息。
如果你的机器使用双显卡,着色器将编译不止一次。有可能第一块显卡编译通过,第二块却编译失败。
 
着色器输入列表
着色器参数名必须能被Panda识别。下面列出所有许可的参数名:
uniform sampler2D tex_0
模型的第一个纹理,要求是普通的纹理。模型使用多个纹理时也可以使用tex_1、tex_2等。如果模型使用3D纹理或cubemap,应该指定为sampler3D或 samplerCUBE。
uniform sampler2D tex_0_suffix
用 连字符将模型的第一个纹理文件名和后缀连接起来得到另一个纹理。例如,如tex_0是 "woman.jpg",则tex_0_normalmap是"woman-normalmap.jpg",tex_0_specular是 "woman-specular.jpg"。模型使用多个纹理时也可以使用tex_1_suffix、tex_2_suffix等。如果模型使用3D纹理 或cubemap,应该指定为sampler3D或 samplerCUBE。
float3 vtx_position: POSITION
顶点的位置,只用于顶点着色器。也可以使用float4, 最后一个分量w==1。
float3 vtx_normal: NORMAL
顶点的法线,只用于顶点着色器。
float4 vtx_color : COLOR
顶点的颜色,只用于顶点着色器。
float2 vtx_texcoord0: TEXCOORD0
模型第一个纹理的纹理坐标,要求是普通的纹理。模型使用多个纹理时也可以使用vtx_texcoord1、 vtx_texcoord2等。只用于顶点着色器。
float3 vtx_tangent0
模型第一个纹理的切向量(tangent vector),只能用在普通的纹理且副法线(binormals)重新计算的情况下。模型使用多个纹理时也可以使用vtx_tangent1、vtx_tangent2等。只用于顶点着色器。
float3 vtx_binormal0
与vtx_texcoord0关联的副法线向量(binormal vector)。只能用在普通的纹理且副法线(binormals)重新计算的情况下。模型使用多个纹理时也可以使用 vtx_binormal1、vtx_binormal2等。只用于顶点着色器。
floatX vtx_anything
可以存储顶点表中任意列的用户数据,请参考GeomVertexData部分。可以使用这种语法来访问这个数据。例如, vtx_chicken 将在顶点数组中搜寻名为"chicken"的列。只用于顶点着色器。
uniform float4x4 trans_x_to_y
从坐标系X到坐标系Y的变换矩阵。详细的解释请参考Shaders and Coordinate Spaces 部分。
uniform float4x4 tpose_x_to_y
trans_x_to_y矩阵的转置矩阵
uniform float4 row0_x_to_y
trans_x_to_y矩阵第0行
uniform float4 row1_x_to_y
trans_x_to_y矩阵第1行
uniform float4 row2_x_to_y
trans_x_to_y矩阵第2行
uniform float4 row3_x_to_y
trans_x_to_y矩阵第3行
uniform float4 col0_x_to_y
trans_x_to_y矩阵第0列
uniform float4 col1_x_to_y
trans_x_to_y矩阵第1列
uniform float4 col2_x_to_y
trans_x_to_y矩阵第2列
uniform float4 col3_x_to_y
trans_x_to_y矩阵第3列
uniform float4x4 mstrans_x
X的模型空间(Model-Space)的变换矩阵,也叫做trans_x_to_model
uniform float4x4 cstrans_x
X的摄影机空间(Camera-Space)的变换矩阵,也叫做trans_x_to_camera
uniform float4x4 wstrans_x
X的世界空间(World-Space)的变换矩阵,也叫做trans_x_to_world
uniform float4 mspos_x
X的模型空间位置(Model-Space Position),也叫做row3_x_to_model
uniform float4 cspos_x
X的摄影机空间位置(Camera-Space Position),也叫做row3_x_to_camera
uniform float4 wspos_x
X的世界空间位置(World-Space Position),也叫做row3_x_to_world
uniform float4x4 mat_modelview
Modelview矩阵
uniform float4x4 inv_modelview
Modelview逆矩阵
uniform float4x4 tps_modelview
Modelview转置矩阵
uniform float4x4 itp_modelview
Modelview逆转置矩阵
uniform float4x4 mat_projection
Projection矩阵
uniform float4x4 inv_projection
Projection逆矩阵
uniform float4x4 tps_projection
Projection转置矩阵
uniform float4x4 itp_projection
Projection逆转置矩阵
uniform float4x4 mat_modelproj
Modelview与Projection复合矩阵
uniform float4x4 inv_modelproj
ModelProj逆矩阵
uniform float4x4 tps_modelproj
ModelProj转置矩阵
uniform float4x4 itp_modelproj
ModelProj逆转置矩阵
uniform float4 k_anything
用 setShaderInput存储的一个常向量,参数k_anythingsetShaderInput("anything", Vec4(x,y,z,w))提供的数据对应。
uniform sampler2d k_anything
用 setShaderInput存储的一个常量纹理,参数k_anythingsetShaderInput("anything", myTex) 提供的数据对应。
uniform float4x4 k_anything
用 setShaderInput存储的一个常量矩阵,参数k_anythingsetShaderInput("anything", myNodePath) 提供的数据对应,该矩阵是nodepath的local 变换。
uniform float2 sys_cardcenter
窗口纹理卡片(window's texture card)中心的纹理坐标,使用(clipx,clipy) * cardcenter + cardcenter生成窗口纹理卡片的纹理坐标。
floatX l_position: POSITION
线性插值后的位置,由顶点着色器向片断着色器输出,在顶点着色器中声明为"out",在片断着色器中声明为"in"。
floatX l_color0: COLOR0
线性插值后的第一颜色。由顶点着色器向片断着色器输出,在顶点着色器中声明为"out",在片断着色器中声明为"in"。
floatX l_color1: COLOR1
线性插值后的第二颜色。由顶点着色器向片断着色器输出,在顶点着色器中声明为"out",在片断着色器中声明为"in"。
floatX l_texcoord0: TEXCOORD0
线性插值后的第0个纹理坐标,由顶点着色器向片断着色器输出,也可以使用l_texcoord1、l_texcoord2等,在顶点着色器中声明为"out",在片断着色器中声明为"in"。
out floatX o_color: COLOR
输出的颜色,由片断着色器向混合单元(blending units)输出,只用于片断着色器。
 
着色器与坐标空间(Shaders and Coordinate Spaces
主要的坐标空间
当编写复杂的着色器时,需要经常进行坐标系变换。为了进行正确的坐标变换,了解panda使用的各个不同的坐标空间非常重要。你必须知道坐标在哪个“空间”里。这里列出几个主要的坐标空间:
模型空间(Model Space:处于模型空间中的坐标以当前正在渲染的模型的中心为参照点。顶点在模型空间排列,因此你使用vtx_position读取的顶点位置为模型空间中的坐标。模型空间为z轴朝上的右手坐标系。
世界坐标(World Space:处于世界空间中的坐标以场景的原点为参照点。世界空间为z轴朝上的右手坐标系。
视空间(View Space:处于视空间中的坐标以摄影机为参考点。视空间为z轴朝上的右手坐标系。
API视空间(API View Space:该坐标空间与视空间一样,区别只是坐标轴可能被翻转以匹配渲染API本身的坐标轴朝向。在OpenGL下,API视空间为y轴朝上的右手坐标系;在DirectX下,API视空间为y轴朝上的左手坐标系。
裁剪空间(Clip Space:Panda的裁剪空间坐标把(X/W, Y/W)映射到一个屏幕像素,(Z/W)映射到一个深度缓冲(depth-buffer)值。该空间的坐标值全都在[-1,1] 范围。
API裁剪空间(API Clip Space:该坐标空间与裁剪空间一样,区别只是坐标轴可能被翻转以匹配渲染API本身的坐标轴朝向,以及坐标值域可能被缩放以满足渲染API的需要。在OpenGL下,(Z/W)值域为[-1,1];在DirectX下,(Z/W)值域为[0,1]。
为着色器提供变换矩阵
你 可以使用名为“trans_x_to_y”的着色器参数自动获得从一个坐标系到另一个坐标系的变换矩阵。x、y可以取“model”、“world”、 “view”、“apiview”、“clip”或“apiclip”。使用这些符号,几乎可以建立你要用到的所有变换矩阵。下面列出部分常用的矩阵,当 然是不完全的:7个关键词组合应该有7x7种可能的矩阵,其中7个为单位矩阵(变换到同一个坐标系)。
目标矩阵
构造的名字
The Modelview Matrix
trans_model_to_apiview
The Projection Matrix
trans_apiview_to_apiclip
the DirectX world matrix
trans_model_to_world
the DirectX view matrix
trans_world_to_apiview
gsg.getCameraTransform()
trans_view_to_world
gsg.getWorldTransform()
trans_world_to_view
gsg.getExternalTransform()
trans_model_to_view
gsg.getInternalTransform()
trans_model_to_apiview
gsg.getCsTransform()
trans_view_to_apiview
gsg.getInvCsTransform()
trans_apiview_to_view
建议:不要使用API视空间或API裁剪空间
“API 视空间”和“API裁剪空间”的坐标系没多少用。它们跟随渲染API变化的特性使人难以适应。当然,你必须使用modelview与projection 的复合矩阵对顶点进行坐标变换,此时,不知不觉就用到了这些空间。但除此以外,我们强烈建议你不要在其他地方使用这些空间。
Model_of_x, View_of_x, Clip_of_x
当在trans指令中使用单词“model”时,隐含的意思是“当前被渲染的模型”。但你可以使用setShaderInput让某个nodepath进入着色器子系统:
myhouse = loader.loadModel("myhouse")
render.setShaderInput('myhouse', myhouse)
接下来在着色器里,你可以从这个nodepath的模型空间变换到其他空间,或者从其他空间变换到该模型空间:
uniform float4x4 trans_world_to_model_of_myhouse
或者,使用缩写的形式:
uniform float4x4 trans_world_to_myhouse
同理,你可以创建一个摄影机,然后传送到着色器子系统。这在进行shadow mapping时特别有用:
render.setShaderInput('shadowcam', self.shadowcam)
现在你使用下面的符号可以把顶点变换到给定的摄影机的裁剪空间:
uniform float4x4 trans_model_to_clip_of_shadowcam
当你把模型的顶点从模型空间变换到shadow摄影机的裁剪空间后,所得到的(X/W,Y/W)当作投影纹理的纹理坐标来把shadow投射到场景中(按比例缩放后的值),(Z/W)值用来与深度缓冲里存储的值进行比较(也是按比例缩放后的值)。
Panda支持标识“trans_x_to_apiclip_of_y”,但再次提醒不要使用它。
使用“view of x”可以把顶点变换到另一个摄影机的视空间。事实上,它和“model of x”一模一样,但当x是一个摄影机时,使用“view of x”更合适。
 
已知的着色器bugs和局限
着色器子系统中已知的bugs
下面列出一些bugs和相应的对策:
问题:寄存器分配
问题:nVidia的Cg编译器尝试给参数分配寄存器。在很多情况下,Cg编译器会把同一个寄存器分配给2个不同的参数,或者是一个参数和一个程序中的常量。
解决:我们发现要是为每个参数提供一条语意字符串,人工地给它分配寄存器,该问题就不会产生。
问题:错误的目标语言(Bad Target Languages
问题:nVidia的Cg编译器将选择将Cg程序翻译成几种“目标”语言中的一种。当Cg编译器要把程序翻译成VP40/FP40语言时,经常生成错误的输出。
解决:我们发现翻译成ARBVP1/ARBFP1最不容易出错。因为每款显卡都必须支持这种语言,因此翻译成它通常很安全。我们Cg编译器提供了一个目标语言的指示:
//Cg profile arbvp1 arbfp1
如果你提供了这条指示,Cg编译器将使用这种推荐的语言。如果你提供了多条指示,它将按顺序尝试每种语言。
问题:未经测试/未完成对DirectX支持
问题:着色器开发目前只能在OpenGL下进行。对DirectX支持一直滞后,也没有经过完备的测试。
解决:Panda默认的设置是使用OpenGL,不是DirectX。到目前为止,当使用着色器时,请不要更改这项设置。