I 顶点着色器基础

声明: 简要翻译shaderX,具体可参看Wolfgang Engel的《shaderX》系列!如原作者对本文的发表感觉不妥,请联系我,我将撤下本文.请勿转载.


你将会学到什么?

  • 编写和编译顶点着色程序.
  • 使用顶点着色器处理光照.
  • 编写和编译像素着色程序.
  • 使用像素着色器处理纹理映射.
  • 纹理效果.
  • 使用像素着色器处理每个像素的光照.


图形渲染管线中的顶点着色器

vertex shader in the pipeline

该图显示了Direct3D渲染管线中一些层次,诸如:源数据操作,顶点操作,像素操作等.

在源数据层中,顶点被组合和曲面细分。

下一层中,Direct3D渲染管线有两种不同的渲染方式组成.

1."固定函数"管线.该管线负责基本的变换与光照,可以使用SetRenderState()函数来操作他们.

2.顶点着色器,这是DX8中引进的新机制.通过灵活的编写顶点着色程序来代替固定功能的函数操作.

目前位置我们的目标是学习顶点着色,所以再往下的分层顶点着色器就无法控制了.


顶点着色器架构

顶点着色器架构

所有的数据在顶点着色器中被表示为128-bit,也就是4个float.

顶点着色器中的数据

    硬件顶点着色器可以被看做是一个SIMD(Single Instruction Multiple Data,“单指令多数据”)处理器,即单条操作指令影响最多4个32-bit的变量.数据格式非常有用,因为大多数的矩阵变换和光照计算都采用4X4矩阵或者4元数.指令非常的简单并且易于理解.顶点着色器不允许任何循环,跳转和条件分支,意思是它只能线性的执行程序.在DX8中每个顶点着色程序最大使用128条指令.组和使用一个处理变换的着色器程序和一个处理光照的着色器程序是不可能的,因为同一时刻只能有一个着色器被激活,并且激活的着色器必须计算每个顶点作为输出数据要求的数据.

     一个顶点着色器使用最大16个输入寄存器来访问输入的顶点数据(v0~v15,每个寄存器大小为128-bit).顶点输入寄存器可以方便的存储标准类型的顶点数据:坐标,法线,漫反射和镜面反射光,雾和点信息.

     在顶点着色器开始执行程序员设定的参数时,常量寄存器的内容由CPU加载.顶点着色器无法去写入常量寄存器.这些寄存器用来存储诸如光照位置,特殊动画效果的过程数据,矩阵,用来变形/关键帧的顶点数据等等.常量寄存器可以辅助地址寄存器a0.x,但是一条指令只能使用一个常量.常量寄存器为(c0~c95),在ATI RADEON 8500 中为c0~c191.

     最大有13个输出寄存器,这依赖于硬件.输出寄存器的命名由字母O开始,代表着输出"output".顶点输出在每次光栅化时可用并且顶点着色程序对它拥有只写属性.最终的结果是另一个顶点,一个在"齐次剪裁空间"的变换后的顶点.


着色器编程

    同一时刻值有一个顶点着色器被激活.所以最好是编写基于任务的顶点着色程序.比如:假设你乘坐的飞船在一个陌生的星球失事.你穿着很普通的盔甲,拿着个锯子,你穿过一个点着蜡烛的递交.一个怪兽出现了,你躲在一个箱子后面,现在你除了思考是不是要拿着锯子做拯救世界的英雄外,你还要考虑,这个场景需要使用的顶点着色器的数量.

    有一个怪兽需要去动态模拟,有可能需要计算他的皮肤反射周围环境中的光.另一个顶点着色器可用于处理地板,墙壁,镜头和烛光.地板墙壁可以使用一个着色器,烛光和镜头则需要各自单独使用一个.这些都取决与你的设计以及对硬件设备的理解.

    使用顶点着色器的步骤:

检查D3DCAPs8::VertexShaderVersion是否支持顶点着色器。

使用D3DVSD_*宏来声明顶点着色器,将顶点缓冲流映射到输入寄存器中。

使用SetVertexShaderConstant()设置顶点着色器的常量寄存器。

使用D3DXAssembleShader*()来编译写好的顶点着色程序。(或者使用着色汇编器预编译着色程序。)

使用CreateVertexShader()创建一个顶点着色器的句柄.

使用SetVertexShader()为特定对象设定顶点着色器.

使用DeleteVertexShader()删除顶点着色器.

 

检查顶点着色器的支持:

//检查是否支持1.1版本的顶点着色器

if( pCaps->VertexShaderVersion < D3DVS_VERSION(1,1) )

  return E_FAIL;


Version:

Functionality:

0.0

DirectX 7

1.0

DirectX 8 without address register A0

1.1

DirectX 8 and DirectX 8.1 with one address register A0

2.0

DirectX 9

 

顶点着色器的声明:

使用前需要先声明顶点着色器:

float c[4] = {0.0f,0.5f,1.0f,2.0f};
DWORD dwDecl0[] = {
  D3DVSD_STREAM(0),
  D3DVSD_REG(0, D3DVSDT_FLOAT3 ),    // 输入寄存器 v0
  D3DVSD_REG(5, D3DVSDT_D3DCOLOR ),  // 输入寄存器 v5
                                     // 设置一些常量
  D3DVSD_CONST(0,1),*(DWORD*)&c[0],*(DWORD*)&c[1],*(DWORD*)&c[2],*(DWORD*)&c[3],
  D3DVSD_END()
};

顶点着色器使用D3DVSD_STREAM(0)声明一个数据流0.在后面的步骤中,使用SetStreamSource()将一个顶点缓冲绑定到一个这里声明的设备数据流。可以使用这种方法对D3D渲染引擎提供不同的数据流。

 

    你必须要声明要处理的顶点数据映射到哪个输入寄存器中。D3DVSD_REG将一个顶点寄存器和一个顶点数据流中的元素绑定。在我们的例子中,D3DVSDT_FLOAT3类型的值被放置在第一个输入寄存器中,D3DVSDT_D3DCOLOR类型的值放在第6个输入寄存器中.举个例子:我们可以通过声明D3DVSD_REG(0, D3DVSDT_FLOAT3 )把寄存器0里的值当作位置数据来处理,通过声明D3DVSD_REG(3,D3DVSD_FLOAT3)把寄存器3中的值当法向量来处理。

    编程者如何把输入顶点的属性映射到不同的寄存器很重要,如果你想要使用N-Patches技术,那么N-Patches曲面细分需要寄存器0的位置数据和寄存器3中的法向量数据.另一方面,编程者可以自由定义合适的映射.

    默认的顶点数据与输入寄存器的映射用于固定函数管线.可查阅d3d8types.h头文件:

    #define D3DVSDE_POSITION             0

#define D3DVSDE_BLENDWEIGHT 1

#define D3DVSDE_BLENDINDICES 2

#define D3DVSDE_NORMAL 3

#define D3DVSDE_PSIZE 4

#define D3DVSDE_DIFFUSE 5

#define D3DVSDE_SPECULAR 6

#define D3DVSDE_TEXCOORD0 7

#define D3DVSDE_TEXCOORD1 8

#define D3DVSDE_TEXCOORD2 9

#define D3DVSDE_TEXCOORD3 10

#define D3DVSDE_TEXCOORD4 11

#define D3DVSDE_TEXCOORD5 12

#define D3DVSDE_TEXCOORD6 13

#define D3DVSDE_TEXCOORD7 14

#define D3DVSDE_POSITION2 15

#define D3DVSDE_NORMAL2 16

D3DVSD_REG的第二个参数指定维度和算法数据类型.以下的值定义在d3d8types.h:

// bit declarations for _Type fields

#define D3DVSDT_FLOAT1 0x00 // 1D float expanded to (value, 0., 0., 1.)

#define D3DVSDT_FLOAT2 0x01 // 2D float expanded to (value, value, 0., 1.)

#define D3DVSDT_FLOAT3 0x02 // 3D float expanded to (value, value, value, 1.)

#define D3DVSDT_FLOAT4 0x03 // 4D float

	// 4D packed unsigned bytes mapped to 0. to 1. range
	// Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A)
	#define D3DVSDT_D3DCOLOR 0x04


#define D3DVSDT_UBYTE4 0x05 // 4D unsigned byte // 2D signed short expanded to (value, value, 0., 1.) #define D3DVSDT_SHORT2 0x06 #define D3DVSDT_SHORT4 0x07 // 4D signed short
    D3DVSD_CONST将常量值加载进顶点着色器的常量寄存器中.第一个参数代表开始填入常量向量的起始寄存器地址.我们设定的是0.第2个参数是要被读取的常量向量.
    一个向量有128-bit宽,所以我们一次使用4个32-bit的FLOAT数据类型.如果你想要加载一个4X4矩阵,可以使用如下语句:
    float c[16] = (0.0f, 0.5f, 1.0f, 2.0f,
			0.0f, 0.5f, 1.0f, 2.0f,
			0.0f, 0.5f, 1.0f, 2.0f,
               		0.0f, 0.5f, 1.0f, 2.0f);
			D3DVSD_CONST(0, 4), *(DWORD*)&c[0],*(DWORD*)&c[1],*(DWORD*)&c[2],*(DWORD*)&c[3],
                    					    *(DWORD*)&c[4],*(DWORD*)&c[5],*(DWORD*)&c[6],*(DWORD*)&c[7],
                    					    *(DWORD*)&c[8],*(DWORD*)&c[9],*(DWORD*)&c[10],*(DWORD*)&c[11],
                    					    *(DWORD*)&c[12],*(DWORD*)&c[13],*(DWORD*)&c[14],*(DWORD*)&c[15],
    D3DVSD_END产生一个END占位符标记顶点着色器声明的结束.另一个例子:
    	float	c[4] = {0.0f,0.5f,1.0f,2.0f};
	DWORD dwDecl[] = {
 	 D3DVSD_STREAM(0),
  	 D3DVSD_REG(0, D3DVSDT_FLOAT3 ), //input register v0
 	 D3DVSD_REG(3, D3DVSDT_FLOAT3 ), // input register v3
 	 D3DVSD_REG(5, D3DVSDT_D3DCOLOR ), // input register v5
 	 D3DVSD_REG(7, D3DVSDT_FLOAT2 ), // input register v7
 	 D3DVSD_CONST(0,1),*(DWORD*)&c[0],*(DWORD*)&c[1],*(DWORD*)&c[2],*(DWORD*)&c[3],
 	 D3DVSD_END()
	};
    如上例:设定数据流0,坐标值可能被绑定到v0,法向量值被绑定到V3,漫反射光被绑定到v5,纹理坐标被绑定到v7,常量寄存器c0读取一个128bit的值.
设定顶点着色器的常量寄存器   
可以使用SetVertexShaderConstant()将值写入常量寄存器,使用GetVertexShaderConstant()读取常量寄存器:
// Set the vertex shader constants
m_pd3dDevice->SetVertexShaderConstant( 0, &vZero, 1 );
m_pd3dDevice->SetVertexShaderConstant( 1, &vOne, 1 );
m_pd3dDevice->SetVertexShaderConstant( 2, &vWeight, 1 );
m_pd3dDevice->SetVertexShaderConstant( 4, &matTranspose, 4 );
m_pd3dDevice->SetVertexShaderConstant( 8, &matCameraTranspose, 4 );
m_pd3dDevice->SetVertexShaderConstant( 12, &matViewTranspose, 4 );
m_pd3dDevice->SetVertexShaderConstant( 20, &fLight, 1 );
m_pd3dDevice->SetVertexShaderConstant( 21, &fDiffuse, 1 );
m_pd3dDevice->SetVertexShaderConstant( 22, &fAmbient, 1 );
m_pd3dDevice->SetVertexShaderConstant( 23, &fFog, 1 );
m_pd3dDevice->SetVertexShaderConstant( 24, &fCaustics, 1 );
m_pd3dDevice->SetVertexShaderConstant( 28, &matProjTranspose, 4 );
SetVertexShaderConstant()的函数原型为:
HRESULT SetVertexShaderConstant(
  DWORD Register,
  CONST void* pConstantData,
  DWORD ConstantCount);
最初,最多有96个常量寄存器可以使用.第一个参数指明常向量被加载的起始寄存器地址,最后一个参数指明被加载的常向量的个数.上例中第一行,vZero将被加载到寄存器0.
matTranspose将被加载进寄存器4,5,6,7.使用SetVertexShaderConstant()D3DVSD_CONST有什么不同?使用宏只能使用一次,而使用函数则在每次调用DrawPrimitive*()前都可调用.

编写并编译一个顶点着色器
语法如下:
OpName dest, [-]s1 [,[-]s2 [,[-]s3]] ;comment
e.g.
mov r1, r2
mad r1, r2, -r3, r4 ; contents of r3 are negated
       
       
InstructionParametersAction
adddest, src1, src2add src1 to src2 (and the optional negation creates substraction)
dp3dest, src1, src2three-component dot product
dest.x = dest.y = dest.z = dest.w = 
(src1.x * src2.x) + (src1.y * src2.y) + (src1.z * src2.z)
dp4dest, src1, src2

four-component dot product
dest.w = (src1.x * src2.x) + (src1.y * src2.y) + (src1.z * src2.z) + (src1.w * src2.w);
dest.x = dest.y = dest.z = unused

What is the difference between dp4 and mul ? dp4 produces a scalar product and mul is a component by component vector product.

dstdest, src1, src2

The dst instruction works like this: The first source operand (src1) is assumed to be the vector (ignored, d*d, d*d, ignored) and the second source operand (src2) is assumed to be the vector (ignored, 1/d, ignored, 1/d).

Calculate distance vector:
dest.x = 1;
dest.y = src1.y * src2.y
dest.z = src1.z
dest.w = src2.w

dst is useful to calculate standard attenuation. Here is a code snippet that might calculate the attenuation for a point light:

; r7.w = distance * distance = (x*x) + (y*y) + (z*z)
dp3 r7.w, VECTOR_VERTEXTOLIGHT, VECTOR_VERTEXTOLIGHT

; VECTOR_VERTEXTOLIGHT.w = 1/sqrt(r7.w)
; = 1/||V|| = 1/distance
rsq VECTOR_VERTEXTOLIGHT.w, r7.w
...
; Get the attenuation
; d = distance
; Parameters for dst:
; src1 = (ignored, d * d, d * d, ignored)
; src2 = (ignored, 1/d, ignored, 1/d)
;
; r7.w = d * d
; VECTOR_VERTEXTOLIGHT.w = 1/d
dst r7, r7.wwww, VECTOR_VERTEXTOLIGHT.wwww 
; dest.x = 1
; dest.y = src0.y * src1.y
; dest.z = src0.z
; dest.w = src1.w
; r7(1, d * d * 1 / d, d * d, 1/d)

; c[LIGHT_ATTENUATION].x = a0
; c[LIGHT_ATTENUATION].y = a1
; c[LIGHT_ATTENUATION].z = a2
; (a0 + a1*d + a2* (d * d)) 
dp3 r7.w, r7, c[LIGHT_ATTENUATION] 
; 1 / (a0 + a1*d + a2* (d * d)) 
rcp ATTENUATION.w, r7.w 
...
; Scale the light factors by the attenuation
mul r6, r5, ATTENUATION.w

exppdest, src.wExponential 10-bit precision
------------------------------------------
float w = src.w; 
float v = (float)floor(src.w);

dest.x = (float)pow(2, v); 
dest.y = w - v;

// Reduced precision exponent 
float tmp = (float)pow(2, w); 
DWORD tmpd = *(DWORD*)&tmp & 0xffffff00; 

dest.z = *(float*)&tmpd; 
dest.w = 1; 
--------------------------------------------
Shortcut:

dest.x = 2 **(int) src.w
dest.y = mantissa(src.w)
dest.z = expp(src.w)
dest.w = 1.0
litdest, src

Calculates lighting coefficients from two dot products and a power. 
---------------------------------------------
To calculate the lighting coefficients, set up the registers as shown:

src.x = N*L ; The dot product between normal and direction to light
src.y = N*H ; The dot product between normal and half vector 
src.z = ignored ; This value is ignored 
src.w = specular power ; The value must be between �128.0 and 128.0 
----------------------------------------------
usage:

dp3 r0.x, rn, c[LIGHT_POSITION]
dp3 r0.y, rn, c[LIGHT_HALF_ANGLE]
mov r0.w, c[SPECULAR_POWER]
lit r0, r0
------------------------------------------------
dest.x = 1.0;
dest.y = max (src.x, 0.0, 0.0);
dest.z= 0.0;
if (src.x > 0.0 && src.w == 0.0)
  dest.z = 1.0;
else if (src.x > 0.0 && src.y > 0.0)
  dest.z = (src.y)
src.w
dest.w = 1.0;

logpdest, src.wLogarithm 10-bit precision
---------------------------------------------------
float v = ABSF(src.w); 
if (v != 0) 

  int p = (int)(*(DWORD*)&v >> 23) - 127;
  dest.x = (float)p;  // exponent

  p = (*(DWORD*)&v & 0x7FFFFF) | 0x3f800000; 
  dest.y = *(float*)&p; // mantissa;

  float tmp = (float)(log(v)/log(2)); 
  DWORD tmpd = *(DWORD*)&tmp & 0xffffff00; 
  dest.z = *(float*)&tmpd;

  dest.w = 1; 

else 

  dest.x = MINUS_MAX(); 
  dest.y = 1.0f; 
  dest.z = MINUS_MAX(); 
  dest.w = 1.0f; 

-----------------------------------------------------
Sortcut: 
dest.x = exponent((int)src.w)
dest.y = mantissa(src.w)
dest.z = log2(src.w)
dest.w = 1.0
maddest, src1, src2, src3dest = (src1 * src2) + src3
maxdest, src1, src2dest = (src1 >= src2)?src1:src2
mindest, src1, src2dest = (src1 < src2)?src1:src2
movdest, src

move
Optimization tip: question every use of mov (try to rap that !), because there might be methods that perform the desired operation directly from the source register or accept the required output register as the destination.

muldest, src1, src2set dest to the component by component product of src1 and src2

; To calculate the Cross Product (r5 = r7 X r8),
; r0 used as a temp
mul r0,-r7.zxyw,r8.yzxw
mad r5,-r7.yzxw,r8.zxyw,-r0

nop do nothing
rcpdest, src.w
if(src.w == 1.0f)
{
  dest.x = dest.y = dest.z = dest.w = 1.0f;
}
else if(src.w == 0)
{
  dest.x = dest.y = dest.z = dest.w = PLUS_INFINITY();
}
else
{
  dest.x = dest.y = dest.z = m_dest.w = 1.0f/src.w;
}

Division:
; scalar r0.x = r1.x/r2.x
RCP r0.x, r2.x
MUL r0.x, r1.x, r0.x
rsqdest, src

reciprocal square root of src
(much more useful than straight 'square root'):

float v = ABSF(src.w);
if(v == 1.0f)
{
  dest.x = dest.y = dest.z = dest.w = 1.0f;
}
else if(v == 0)
{
  dest.x = dest.y = dest.z = dest.w = PLUS_INFINITY();
}
else
{
  v = (float)(1.0f / sqrt(v));
  dest.x = dest.y = dest.z = dest.w = v;
}

Square root:
; scalar r0.x = sqrt(r1.x)
RSQ r0.x, r1.x
MUL r0.x, r0.x, r1.x
sgedest, src1, src2

dest = (src1 >=src2) ? 1 : 0

useful to mimic conditional statements:
; compute r0 = (r1 >= r2) ? r3 : r4
; one if (r1 >= r2) holds, zero otherwise
SGE r0, r1, r2 
ADD r1, r3, -r4 
; r0 = r0*(r3-r4) + r4 = r0*r3 + (1-r0)*r4
; effectively, LERP between extremes of r3 and r4
MAD r0, r0, r1, r4

sltdest, src1, src2dest = (src1 < src2) ? 1 : 0
顶点着色器的运算逻辑单元(ALU)是一个多线程向量处理器.它包含两个函数单元.SIMD向量单元负责mov,mul,mad,dp3,dp4,dst,min,max,slt,和sge指令.
特殊函数单元负责rcp,rsq,log,exp,和lit指令.大部分的指令花费一个指令周期,rcp和rsq在特定情况下将花费一个以上的周期.

应用提示

Rsq用于在光照方程中标准化向量。指数指令expp可用于雾效,过程噪声产生(见NVDIA柏林噪声(perlin noise)示例),例子系统中的例子行为,游戏中物体的损伤等。你会在任何需要快速变换的函数中使用它。如果你需要非常缓慢的增长,可以使用对数函数logp。可以用log函数做指数函数的反操作。Lit指令主要用来处理方向光照。它计算漫反射和镜面反射因素,这些因素基于N*LN*H以及镜面反射强度。虽然这里没有包括衰减,但可以使用dst指令,操作lit指令的结果和一个衰减因素来完成衰减计算。这个命令对点光源和放射光源构造衰减因素很有用。Minmax指令可用于计算绝对值。

顶点着色器中的复杂指令


顶点着色器支持复杂指令,术语“macro”用于这些指令有些不合适,因为这些指令不是简单的C预处理器宏的子集。你要小心使用这些指令。如果你使用他们,那么你可能失去对128条指令上线的控制和一些可能的优化。另一方面,软件模拟模式中,InterAMD的处理器可能会优化一个m4x4复杂指令。所以,如果你需要4dp4调用,也许使用一个m4X4复杂指令是一个好注意。如果你决定使用m4X4指令,那么对于同一数据不应该再使用dp4指令,因为他们的结果会有微妙的不同


Macro

Parameters

Action

Clocks

expp

dest, src1

provides exponential with full precision to at least 1/220

12

frc

dest, src1

returns fractional portion of each input component

3

log

dest, src1

provides log2(x) with full float precision of at least 1/220

12

m3x2

dest, src1, src2

computes the product of the input vector and a 3x2 matrix

2

m3x3

dest, src1, src2

computes the product of the input vector and a 3x3 matrix

3

m3x4

dest, src1, src2

computes the product of the input vector and a 3x4 matrix

4

m4x3

dest, src1, src2

computes the product of the input vector and a 4x3 matrix

3

m4x4

dest, src1, src2

computes the product of the input vector and a 4x4 matrix

4

上表word格式文件下载链接

使用这些指令可以完成所有变换和光照操作。你甚至可以用他们写一个自定义的固定函数管线

综合运用

现在让我们看看在顶点着色器的ALU中,这些寄存器和指令如何工作的

1.1版本的顶点着色器中,一次光栅化最大有16个输入寄存器,96个常量寄存器,12个临时寄存器,1个地址寄存器,和最多13个输出寄存器可用。每个寄存器可以处理4x32bit的值。每个32-bit的值可通过xyzw子集访问。如果有一个包含xyzw128bit的值。访问相应数据可以在寄存器的名字后面加上“.”和x/y/z/w


使用输入寄存器

v0v15一共16个输入寄存器。他们处理的典型值是

位置信息(x,y,z,w

漫反射光(r,g,b,a->0.0~1.0

镜面反射光(r,g,b,a->0.0~1.0

最大8个纹理坐标(s,t,r,qu,v,w,q),一般是4个或6个,取决于硬件支持。

雾化(f,*,*,*->值用于雾化方程

点尺寸(p,*,*,*


你可以使用v0.x读取位置信息的x部分,v0.y读取y部分。如果需要知道漫反射光的RGBA结构中的绿色部分,可以检查v1.y.如果你读取雾化值可以使用v7.x,其余的v7.y,v7.z,v7.w是无用的.输入寄存器是只读的.每条指令只能访问一个顶点输入寄存器。未指定的值在寄存器中默认值为0.0.下面的例子演示v0c0~c3的点乘,结果存储在oPos

dp4 oPos.x , v0 , c0
dp4 oPos.y , v0 , c1
dp4 oPos.z , v0 , c2
dp4 oPos.w , v0 , c3

像这样的代码段常出现在使用现有的世界坐标系矩阵,视口坐标系矩阵和投影坐标系矩阵将投影空间的映射到剪裁空间。点乘进行如下操作

oPos.x = (v0.x * c0.x) + (v0.y * c0.y) + (v0.z * c0.z) + (v0.w * c0.w)

假设我们使用单位向量,可知点乘的两个向量范围永远在[-1,1].所以oPos的值永远在这个区间内.你也可以使用如下指令:

4x4 oPos, v0 , c0

记住一条指令只能使用一个输入寄存器.

在属于寄存器中的值在运算后还会保留,这意味着下一个顶点着色器程序可重用这些数据.


使用常量寄存器

常量寄存器的典型使用包阔:

矩阵数据:4X4矩阵

光照属性(比如位置,衰减等.)

当前时间

顶点差值数据

例程数据

常量寄存器对于顶点着色器是只读的,但在应用程序中可读写它.常量寄存器的值保留时间与输入寄存器相同,所以他们也可以被重用.这就避免了程序中频繁的使用SetVertexShaderConstant()函数.读取超出范围的常量寄存器返回值(0.0, 0.0, 0.0, 0.0).

每条指令只能使用一个常量寄存器,但可以使用多次.例如:

; 合法

mul r5, c11, c11 ; The product of c11 and c11 is stored in r5
; 不合法
add v0, c4, c3

一个合法但更复杂的运用:

; dest = (src1 * src2) + src3
mad r0, r0, c20, c20 ; multiplies r0 with c20 and adds c20 to the result

使用地址寄存器

使用a0~aN(vs1.1以上版本可用超过1个地址寄存器)访问地址寄存器.vs1.1中使用a0的唯一用法是指定常量寄存器的偏移:

c[a0.x + n] ; 仅在vs1.1或更高版本支持
            ; n 是基址a0.x 是偏移

下面是一个使用地址寄存器的例子:

//Set 1
mov a0.x,r1.x
m4x3 r4,v0,c[a0.x + 9];
m3x3 r5,v3,c[a0.x + 9];

不同的常量寄存器使用不同的指令,这取决于临时寄存器r1.x.请注意,a0只存储正数,a0.xa0唯一可用的组件,并且只能使用mov指令写a0.x.

使用临时寄存器

可通过使用r0~r11访问12个临时寄存器.下面是例子:

dp3 r2, r1, -c4 ; A three-component dot product: dest.x = dest.y = dest.z 

;= dest.w = (r1.x * -c4.x) + (r1.y * -c4.y) + (r1.z * -c4.z)

            ...

mov r0.x, v0.x

mov r0.y, c4.w

mov r0.z, v0.y

mov r0.w, c4.w


如果试图读一个没有赋值的常量寄存器,在创建顶点着色器时会返回一条错误信息.


使用输出寄存器

最多有13个只写输出寄存器.他们作为光栅化的输入.每个输出寄存器使用小写字母”o”作为前缀并且以他们在定点着色中的用途命名:

Name

Value

Description

oDn

2 quad-floats

Output color data directly to the pixel shader. Required for diffuse color (oD0) and specular color (oD1).

oPos

1 quad-float

Output position in homogenous clipping space. Must be written by the vertex shader.

oTn

up to 8 quad-floats
Geforce 3: 4
RADEON 8500: 6

Output texture coordinates. Required for maximum number of textures simultaneously bound to the texture blending stage.

oPts.x

1 scalar float

Output point-size registers. Only the scalar x-component of the point size is functional

oFog.x

1 scalar float

the fog factor to be interpolated and then routed to the fog table. Only the scalar x-component is functional.

下面是的例子演示了如何使用oPos,oD0oT0寄存器:

dp4 oPos.x , v0 , c4 ; x的放射投影
dp4 oPos.y , v0 , c5 ; y的放射投影
dp4 oPos.z , v0 , c6 ; z的放射投影
dp4 oPos.w , v0 , c7 ; w的放射投影
mov oD0, v5          ; 设定漫反射光颜色
mov oT0, v2 ; 从输入寄存器 v2输出纹理坐标到oT0

上面显示的是使用四个dp4指令完成从投影到剪裁空间的映射.第一个mov指令将v5的内容传送到颜色输出寄存器,第二个mov指令将v2内容传送到纹理输出寄存器.

使用oFog.x输出寄存器的示例:

; Scale by fog parameters :
; c5.x = fog start
; c5.y = fog end
; c5.z = 1/range
; c5.w = fog max
dp4 r2, v0, c2 ; r2 = 摄像机距离
sge r3, c0, c0 ; r3 = 1
add r2, r2, -c5.x           ; 摄像机深度 (z) - fog start
mad r3.x, -r2.x, c5.z, r3.x ; 1.0 - (z - fog start) * 1/range
                            ; fog=1.0 意味着没有雾, 
                            ; fog=0.0 意味着雾最大
max oFog.x, c5.w, r3.x      ; clamp the fog with our custom max value

每个顶点着色器必须至少写入oPos的一个组件(.x/.y/.z/.w),否则会得到一个汇编器产生的错误信息.

    当使用顶点着色器时,D3DTSS_TEXCOORDINDEXD3DTSS_TCI_*标志位被忽略.所有的纹理坐标按数字顺序被映射.优化提示:尽早的输出oPos以触发像素着色器的并行执行.尝试重排列顶点着色程序的指令来达到此目的.

所有从顶点着色器输出的值区间为[0,1].如果在像素着色器中需要有符号的值,必须在顶点着色器中压缩,然后在像素着色器中使用_bx2重新展开.

 交换(swizzling)和掩码(masking)

如果你把输入,常量,临时寄存器做源寄存器,你可以使用交换功能.如果你把输出,临时寄存器当作目的寄存器,可以使用掩码功能.

交换非常的高效,比如可以快速的把(0.5, 0.0, 1.0, 0.6)转换为(0.0, 0.0, 1.0, 0.0)或者(0.6, 1.0, -0.5, 0.6)。.
所有在指令中做源寄存器的寄存器都可使用交换:

 

mov R1, R2.wxyz;

swizzling

mov R1, -R2.xyyz

swizzling2

掩码

看例子:

mov  R1.x, R2


只有x部分被写入R1.


mov  R1.xw, R2

只有R2xw部分被写入R1,目标寄存器不支持交换和负数.

编写顶点着色程序的指南

最重要的约束如下:

至少要写入输出寄存器oPos中的一个组件。

每个程序最大128条指令。

每个指令只能使用一个常量寄存器。

每个指令只能使用一个输入寄存器。

没有C风格的条件语句。但可以使用sge指令模仿r0 = (r1 >= r2) ? r3 : r4

所有从顶点着色器输出的值区间为[0,1]

 一些优化顶点着色器的方法

  • 读关于Kim Pallister关于优化顶点着色程序的文章。
  • 使用SetVertexShaderConstant()设定顶点着色器的常量寄存器。
  • 尽量避免使用mov指令。
  • 简化指令:
mad   r4,r3,c9,r4
mov   oD0,r4
==
mad   oD0,r3,c9,r4

优化前分解指令中的复杂指令。

一个关于平衡Gpu/Cpu加载平衡的规则:很多着色的计算可以按照每个对象的方式代替每个顶点放入常量寄存器.cpu计算对象,然后把结果当作一个常量送给Gpu,比全部在Gpu中计算要快的多.


编译一个顶点着色程序

Direct3D使用字符编码,OpenGL使用字符串.因此D3D开发者需要使用汇编器来汇编代码.

有三种方式编译一个顶点着色程序:

 

使用ASCII编码写顶点着色源程序,比如text.vsh,使用顶点着色器汇编器讲它编译成二进制文件,比如text.vso.这个文件将会在游戏开始被打开和读取.

使用ASCII编码写顶点着色源程序,存为cpp文件,在程序开始时使用D3DXAssembleShader*()加载编译.

在效果文件中编写顶点着色程序,并在程序开始时打开这个效果文件. D3DXCreateEffectFromFile()用于打开效果文件.也可以预编译效果文件


创建一个顶点着色器

HRESULT CreateVertexShader(
CONST DWORD* pDeclaration,
CONST DWORD* pFunction,
DWORD* pHandle,
DWORD Usage);

该函数用于创建和使用一个顶点着色程序.pDeclaration是一个指向着色器声明的指针.pFunction指向被编译的着色器程序.pHandle返回函数成功后得到的着色器句柄.Usage可以强制软件方式顶点着色(D3DUSAGE_SOFTWAREPROCESSING).D3DRS_SOFTWAREVERTEXPROCESSING被设为true时为启用.但是使用软件方式着色没有硬件方式快.

设置顶点着色器

可以为特定对象设置顶点着色器,只需在对象的DrawPrimitive*()调用前执行SetVertexShader().该函数可在图元间动态调用顶点着色程序.

// set the vertex shader
m_pd3dDevice->SetVertexShader( m_dwVertexShader );

该函数的参数是CreateVertexShader()返回的句柄。有多少个顶点,顶点着色程序就要执行多少次。例如你想要显示一个旋转的方形,你使用4个顶点和一个索引表,在NVDIA 着色调试器中,顶点着色器程序在DrawPrimitive*()调用前运行了4


释放顶点着色器资源

当游戏结束时,被顶点着色器占用的资源必须被释放。

// 删除顶点着色器
if (m_pd3dDevice->m_dwVertexShader != 0xffffffff)
{
m_pd3dDevice->DeleteVertexShader( m_dwVertexShader );
m_pd3dDevice->m_dwVertexShader = 0xffffffff;
}

 

(完)PS:不知道怎么回事,这些排版是自动编辑的 我改不过来,无语。。。。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基本信息 原书名:WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) 原出版社: Addison-Wesley Professional 作者: (美)Kouichi Matsuda Rodger Lea(松田浩一,罗杰.李) 译者: 谢光磊 出版社:电子工业出版社 ISBN:9787121229428 上架时间:2014-6-11 出版日期:2014 年6月 开本:16开 页码:470 版次:1-1 --------------------- 目录 《WebGL编程指南》 第1 章 WebGL 概述 1 WebGL 的优势 3 使用文本编辑器开发三维应用 3 轻松发布三维图形程序 4 充分利用浏览器的功能 5 学习和使用WebGL 很简单 5 WebGL 的起源 5 WebGL 程序的结构 6 总结 7 第2 章 WebGL 入门 9 Canvas 是什么? 10 使用[canvas] 标签 11 DrawRectangle.js 13 最短的WebGL 程序:清空绘图区 16 HTML 文件(HelloCanvas.html) 16 JavaScript 程序(HelloCanvas.js) 17 用示例程序做实验 22 绘制一个点(版本1) 22 HelloPoint1.html 24 HelloPoint1.js 24 着色器是什么? 25 使用着色器的WebGL 程序的结构 27 初始化着色器 29 顶点着色器 31 片元着色器 33 绘制操作 34 WebGL 坐标系统 35 用示例程序做实验 37 绘制一个点(版本2) 38 使用attribute 变量 38 示例程序(HelloPoint2.js) 39 获取attribute 变量的存储位置 41 向attribute 变量赋值 42 gl.vertexAttrib3f() 的同族函数 44 用示例程序做实验 45 通过鼠标点击绘点 46 示例程序(ClickedPoints.js) 47 注册事件响应函数 48 响应鼠标点击事件 50 用示例程序做实验 53 改变点的颜色 55 示例程序(ColoredPoints.js) 56 uniform 变量 58 获取uniform 变量的存储地址 59 向uniform 变量赋值 60 gl.uniform4f() 的同族函数 61 总结 62 第3 章 绘制和变换三角形 63 绘制多个点 64 示例程序(MultiPoint.js) 66 使用缓冲区对象 69 创建缓冲区对象(gl.createBuffer()) 70 绑定缓冲区(gl.bindBuffer()) 71 向缓冲区对象中写入数据(gl.bufferData()) 72 类型化数组 74 将缓冲区对象分配给attribute 变量(gl.vertexAttribPointer()) 75 开启attribute 变量(gl.enableVertexAttribArray()) 77 gl.drawArrays() 的第2 个和第3 个参数 78 用示例程序做实验 79 Hello Triangle 80 示例程序(HelloTriangle.js) 80 基本图形 82 用示例程序做实验 83 Hello Rectangle(HelloQuad) 84 用示例程序做实验 85 移动、旋转和缩放 86 平移 87 示例程序(TranslatedTriangle.js) 88 旋转 91 示例程序(RotatedTriangle.js) 93 变换矩阵:旋转 97 变换矩阵:平移 100 4×4 的旋转矩阵 101 示例程序(RotatedTriangle_Matrix.js) 102 平移:相同的策略 105 变换矩阵:缩放 106 总结 108 第4 章 高级变换与动画基础 109 平移,然后旋转 109 矩阵变换库:cuon-matrix.js 110 示例程序(RotatedTriangle_Matrix4.js) 111 复合变换 113 示例程序(RotatedTranslatedTriangle.js) 115 用示例程序做实验 117 动画 118 动画基础 119 示例程序(RotatingTriangle.js) 119 反复调用绘制函数(tick()) 123 按照指定的旋转角度绘制三角形(draw()) 123 请求再次被调用(requestAnimationFrame()) 125 更新旋转角(animate()) .126 用示例程序做实验 128 总结 130 第5 章 颜色与纹理 131 将非坐标数据传入顶点着色器 131 示例程序(MultiAttributeSize.js) 133 创建多个缓冲区对象 134 gl.vertexAttribPointer() 的步进和偏移参数 135 示例程序(MultiAttributeSize_Interleaved.js) 136 修改颜色(varying 变量) 140 示例程序(MultiAttributeColor.js) 141 用示例程序做实验 144 彩色三角形(ColoredTriangle.js) 145 几何形状的装配和光栅化 145 调用片元着色器 149 用示例程序做实验 149 varying 变量的作用和内插过程 151 在矩形表面贴上图像 153 纹理坐标 156 将纹理图像粘贴到几何图形上 156 示例程序(TexturedQuad.js) 157 设置纹理坐标(initVertexBuffers()) 160 配置和加载纹理(initTextures()) 160 为WebGL 配置纹理(loadTexture()) 164 图像Y 轴反转 164 激活纹理单元(gl.activeTexture()) 165 绑定纹理对象(gl.bindTexture()) 166 配置纹理对象的参数(gl.texParameteri()) 168 将纹理图像分配给纹理对象(gl.texImage2D()) 171 将纹理单元传递给片元着色器(gl.uniform1i()) 173 从顶点着色器向片元着色器传输纹理坐标 174 在片元着色器中获取纹理像素颜色(texture2D()) 174 用示例程序做试验 175 使用多幅纹理 177 示例程序(MultiTexture.js) 178 总结 183 第6 章 OpenGL ES 着色器语言(GLSL ES) 185 回顾:基本着色器代码 186 GLSL ES 概述 186 你好,着色器! 187 基础 187 执行次序 187 注释 187 数据值类型(数值和布尔值) 188 变量 188 GLSL ES 是强类型语言 189 基本类型 189 赋值和类型转换 190 运算符 191 矢量和矩阵 192 赋值和构造 193 访问元素 195 运算符 197 结构体 200 赋值和构造 200 访问成员 200 运算符 201 数组 201 取样器(纹理) 202 运算符优先级 203 程序流程控制:分支和循环 203 if 语句和if-else 语句 203 for 语句 204 continue、break 和discard 语句 205 函数 205 规范声明 207 参数限定词 207 内置函数 208 全局变量和局部变量 209 存储限定字 209 const 变量 209 Attribute 变量 210 uniform 变量 211 varying 变量 211 精度限定字 211 预处理指令 213 总结 215 第7 章 进入三维世界 217 立方体由三角形构成 217 视点和视线 218 视点、观察目标点和上方向 219 示例程序(LookAtTriangles.js) 221 LookAtTriangles.js 与RotatedTriangle_Matrix4.js 224 从指定视点观察旋转后的三角形 225 示例程序(LookAtRotatedTriangles.js) 227 用示例程序做实验 228 利用键盘改变视点 230 示例程序(LookAtTrianglesWithKeys.js) 230 独缺一角 232 可视范围(正射类型) 233 可视空间 234 定义盒状可视空间 235 示例程序(OrthoView.html) 236 示例程序(OrthoView.js) 237 JavaScript 修改HTML 元素 239 顶点着色器的执行流程 239 修改near 和far 值 241 补上缺掉的角(LookAtTrianglesWithKeys_ViewVolume.js) 243 用示例程序做实验 245 可视空间(透视投影) 246 定义透视投影可视空间 247 示例程序(perspectiveview.js) 249 投影矩阵的作用 251 共冶一炉(模型矩阵、视图矩阵和投影矩阵) 252 示例程序(PerspectiveView_mvp.js) 254 用示例程序做实验 257 正确处理对象的前后关系 258 隐藏面消除 260 示例程序(DepthBuffer.js) 262 深度冲突 263 立方体 266 通过顶点索引绘制物体 268 示例程序(HelloCube.js) 268 向缓冲区中写入顶点的坐标、颜色与索引 271 为立方体的每个表面指定颜色 274 示例程序(ColoredCube.js) 275 用示例程序做实验 277 总结 279 第8 章 光照 281 光照原理 281 光源类型 283 反射类型 284 平行光下的漫反射 286 根据光线和表面的方向计算入射角 287 法线:表面的朝向 288 示例程序(LightedCube.js) 291 环境光下的漫反射 296 示例程序(LightedCube_ambient.js) 298 运动物体的光照效果 299 魔法矩阵:逆转置矩阵 301 示例程序(LightedTranslatedRotatedCube.js) 302 点光源光 304 示例程序(PointLightedCube.js) 305 更逼真:逐片元光照 308 示例程序(PointLightedCube_perFragment.js) 309 总结 310 第9 章 层次模型 311 多个简单模型组成的复杂模型 311 层次结构模型 313 单关节模型 314 示例程序(JointMode.js) 315 绘制层次模型(draw()) 319 多节点模型 321 示例程序(MultiJointModel.js) 323 绘制部件(drawBox()) 326 绘制部件(drawSegments()) 327 着色器着色器程序对象:initShaders() 函数的作用 332 创建着色器对象(gl.createShader()) 333 指定着色器对象的代码(gl.shaderSource()) 334 编译着色器(gl.compileShader()) 334 创建程序对象(gl.createProgram()) 336 为程序对象分配着色器对象(gl.attachShader()) 337 连接程序对象(gl.linkProgram()) 337 告知WebGL 系统所使用的程序对象(gl.useProgram()) 339 initShaders() 函数的内部流程 339 总结 342 第10 章 高级技术 343 用鼠标控制物体旋转 343 如何实现物体旋转 344 示例程序(RotateObject.js) 344 选中物体 347 如何实现选中物体 347 示例程序(PickObject.js) 348 选中一个表面 351 示例程序(PickFace.js) 352 HUD(平视显示器) 355 如何实现HUD 355 示例程序(HUD.html) 356 示例程序(HUD.js) 357 在网页上方显示三维物体 359 雾化(大气效果) 359 如何实现雾化 360 示例程序(Fog.js) 361 使用w 分量(Fog_w.js) 363 绘制圆形的点 364 如何实现圆形的点 364 示例程序(RoundedPoint.js) 366 α 混合 367 如何实现α 混合 367 示例程序(LookAtBlendedTriangles.js) 369 混合函数 369 半透明的三维物体(BlendedCube.js) 371 透明与不透明物体共存 372 切换着色器 373 如何实现切换着色器 374 示例程序(ProgramObject.js) 375 渲染到纹理 379 帧缓冲区对象和渲染缓冲区对象 380 如何实现渲染到纹理 381 示例程序(FramebufferObject.js) 382 创建帧缓冲区对象(gl.createFramebuffer()) 385 创建纹理对象并设置其尺寸和参数 385 创建渲染缓冲区对象(gl.createRenderbuffer()) 386 绑定渲染缓冲区并设置其尺寸(gl.bindRenderbuffer(), gl.renderbufferStorage()) 386 将纹理对象关联到帧缓冲区对象(gl.bindFramebuffer(), gl.framebufferTexture2D()) 388 将渲染缓冲区对象关联到帧缓冲区对象(gl.framebufferRenderbuffer()) 389 检查帧缓冲区的配置(gl.checkFramebufferStatus()) 390 在帧缓冲区进行绘图 390 绘制阴影 392 如何实现阴影 392 示例程序(Shadow.js) 393 提高精度 399 示例程序(Shadow_highp.js) 400 加载三维模型 401 OBJ 文件格式 404 MTL 文件格式 405 示例程序(OBJViewer.js) 406 自定义类型对象 409 示例程序(OBJViewer.js 解析数据部分) 411 响应上下文丢失 418 如何响应上下文丢失 419 示例程序(RotatingTriangle_contextLost.js) 420 总结 422 附录A WebGL 中无须交换缓冲区 423 附录B GLSL ES 1.0 内置函数 427 角度和三角函数 428 指数函数 429 通用函数 430 几何函数 433 矩阵函数 434 矢量函数 435 纹理查询函数 436 附录C 投影矩阵 437 正射投影矩阵 437 透视投影矩阵 437 附录D WebGL/OpenGL :左手还是右手坐标系? 439 示例程序(CoordinateSystem.js) 440 隐藏面消除和裁剪坐标系统 443 裁剪坐标系和可视空间 444 什么是对的? 446 总结 448 附录E 逆转置矩阵 449 附录F 从文件中加载着色器 453 附录G 世界坐标系和本地坐标系 . 457 本地坐标系 458 世界坐标系 459 变换与坐标系 461 附录H WebGL 的浏览器设置 . 463
基本信息 原书名:WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) 原出版社: Addison-Wesley Professional 作者: (美)Kouichi Matsuda Rodger Lea(松田浩一,罗杰.李) 译者: 谢光磊 出版社:电子工业出版社 ISBN:9787121229428 上架时间:2014-6-11 出版日期:2014 年6月 开本:16开 页码:470 版次:1-1 --------------------- 目录 《WebGL编程指南》 第1 章 WebGL 概述 1 WebGL 的优势 3 使用文本编辑器开发三维应用 3 轻松发布三维图形程序 4 充分利用浏览器的功能 5 学习和使用WebGL 很简单 5 WebGL 的起源 5 WebGL 程序的结构 6 总结 7 第2 章 WebGL 入门 9 Canvas 是什么? 10 使用[canvas] 标签 11 DrawRectangle.js 13 最短的WebGL 程序:清空绘图区 16 HTML 文件(Hel loCanvas.html) 16 JavaScript 程序(HelloCanvas.js) 17 用示例程序做实验 22 绘制一个点(版本1) 22 HelloPoint1.html 24 HelloPoint1.js 24 着色器是什么? 25 使用着色器的WebGL 程序的结构 27 初始化着色器 29 顶点着色器 31 片元着色器 33 绘制操作 34 WebGL 坐标系统 35 用示例程序做实验 37 绘制一个点(版本2) 38 使用attribute 变量 38 示例程序(HelloPoint2.js) 39 获取attribute 变量的存储位置 41 向attribute 变量赋值 42 gl.vertexAttrib3f() 的同族函数 44 用示例程序做实验 45 通过鼠标点击绘点 46 示例程序(ClickedPoints.js) 47 注册事件响应函数 48 响应鼠标点击事件 50 用示例程序做实验 53 改变点的颜色 55 示例程序(ColoredPoints.js) 56 uniform 变量 58 获取uniform 变量的存储地址 59 向uniform 变量赋值 60 gl.uniform4f() 的同族函数 61 总结 62 第3 章 绘制和变换三角形 63 绘制多个点 64 示例程序(MultiPoint.js) 66 使用缓冲区对象 69 创建缓冲区对象(gl.createBuffer()) 70 绑定缓冲区(gl.bindBuffer()) 71 向缓冲区对象中写入数据(gl.bufferData()) 72 类型化数组 74 将缓冲区对象分配给attribute 变量(gl.vertexAttribPointer()) 75 开启attribute 变量(gl.enableVertexAttribArray()) 77 gl.drawArrays() 的第2 个和第3 个参数 78 用示例程序做实验 79 Hello Triangle 80 示例程序(HelloTriangle.js) 80 基本图形 82 用示例程序做实验 83 Hello Rectangle(HelloQuad) 84 用示例程序做实验 85 移动、旋转和缩放 86 平移 87 示例程序(TranslatedTriangle.js) 88 旋转 91 示例程序(RotatedTriangle.js) 93 变换矩阵:旋转 97 变换矩阵:平移 100 4×4 的旋转矩阵 101 示例程序(RotatedTriangle_Matrix.js) 102 平移:相同的策略 105 变换矩阵:缩放 106 总结 108 第4 章 高级变换与动画基础 109 平移,然后旋转 109 矩阵变换库:cuon-matrix.js 110 示例程序(RotatedTriangle_Matrix4.js) 111 复合变换 113 示例程序(RotatedTranslatedTriangle.js) 115 用示例程序做实验 117 动画 118 动画基础 119 示例程序(RotatingTriangle.js) 119 反复调用绘制函数(tick()) 123 按照指定的旋转角度绘制三角形(draw()) 123 请求再次被调用(requestAnimationFrame()) 125 更新旋转角(animate()) .126 用示例程序做实验 128 总结 130 第5 章 颜色与纹理 131 将非坐标数据传入顶点着色器 131 示例程序(MultiAttributeSize.js) 133 创建多个缓冲区对象 134 gl.vertexAttribPointer() 的步进和偏移参数 135 示例程序(MultiAttributeSize_Interleaved.js) 136 修改颜色(varying 变量) 140 示例程序(MultiAttributeColor.js) 141 用示例程序做实验 144 彩色三角形(ColoredTriangle.js) 145 几何形状的装配和光栅化 145 调用片元着色器 149 用示例程序做实验 149 varying 变量的作用和内插过程 151 在矩形表面贴上图像 153 纹理坐标 156 将纹理图像粘贴到几何图形上 156 示例程序(TexturedQuad.js) 157 设置纹理坐标(initVertexBuffers()) 160 配置和加载纹理(initTextures()) 160 为WebGL 配置纹理(loadTexture()) 164 图像Y 轴反转 164 激活纹理单元(gl.activeTexture()) 165 绑定纹理对象(gl.bindTexture()) 166 配置纹理对象的参数(gl.texParameteri()) 168 将纹理图像分配给纹理对象(gl.texImage2D()) 171 将纹理单元传递给片元着色器(gl.uniform1i()) 173 从顶点着色器向片元着色器传输纹理坐标 174 在片元着色器中获取纹理像素颜色(texture2D()) 174 用示例程序做试验 175 使用多幅纹理 177 示例程序(MultiTexture.js) 178 总结 183 第6 章 OpenGL ES 着色器语言(GLSL ES) 185 回顾:基本着色器代码 186 GLSL ES 概述 186 你好,着色器! 187 基础 187 执行次序 187 注释 187 数据值类型(数值和布尔值) 188 变量 188 GLSL ES 是强类型语言 189 基本类型 189 赋值和类型转换 190 运算符 191 矢量和矩阵 192 赋值和构造 193 访问元素 195 运算符 197 结构体 200 赋值和构造 200 访问成员 200 运算符 201 数组 201 取样器(纹理) 202 运算符优先级 203 程序流程控制:分支和循环 203 if 语句和if-else 语句 203 for 语句 204 continue、break 和discard 语句 205 函数 205 规范声明 207 参数限定词 207 内置函数 208 全局变量和局部变量 209 存储限定字 209 const 变量 209 Attribute 变量 210 uniform 变量 211 varying 变量 211 精度限定字 211 预处理指令 213 总结 215 第7 章 进入三维世界 217 立方体由三角形构成 217 视点和视线 218 视点、观察目标点和上方向 219 示例程序(LookAtTriangles.js) 221 LookAtTriangles.js 与RotatedTriangle_Matrix4.js 224 从指定视点观察旋转后的三角形 225 示例程序(LookAtRotatedTriangles.js) 227 用示例程序做实验 228 利用键盘改变视点 230 示例程序(LookAtTrianglesWithKeys.js) 230 独缺一角 232 可视范围(正射类型) 233 可视空间 234 定义盒状可视空间 235 示例程序(OrthoView.html) 236 示例程序(OrthoView.js) 237 JavaScript 修改HTML 元素 239 顶点着色器的执行流程 239 修改near 和far 值 241 补上缺掉的角(LookAtTrianglesWithKeys_ViewVolume.js) 243 用示例程序做实验 245 可视空间(透视投影) 246 定义透视投影可视空间 247 示例程序(perspectiveview.js) 249 投影矩阵的作用 251 共冶一炉(模型矩阵、视图矩阵和投影矩阵) 252 示例程序(PerspectiveView_mvp.js) 254 用示例程序做实验 257 正确处理对象的前后关系 258 隐藏面消除 260 示例程序(DepthBuffer.js) 262 深度冲突 263 立方体 266 通过顶点索引绘制物体 268 示例程序(HelloCube.js) 268 向缓冲区中写入顶点的坐标、颜色与索引 271 为立方体的每个表面指定颜色 274 示例程序(ColoredCube.js) 275 用示例程序做实验 277 总结 279 第8 章 光照 281 光照原理 281 光源类型 283 反射类型 284 平行光下的漫反射 286 根据光线和表面的方向计算入射角 287 法线:表面的朝向 288 示例程序(LightedCube.js) 291 环境光下的漫反射 296 示例程序(LightedCube_ambient.js) 298 运动物体的光照效果 299 魔法矩阵:逆转置矩阵 301 示例程序(LightedTranslatedRotatedCube.js) 302 点光源光 304 示例程序(PointLightedCube.js) 305 更逼真:逐片元光照 308 示例程序(PointLightedCube_perFragment.js) 309 总结 310 第9 章 层次模型 311 多个简单模型组成的复杂模型 311 层次结构模型 313 单关节模型 314 示例程序(JointMode.js) 315 绘制层次模型(draw()) 319 多节点模型 321 示例程序(MultiJointModel.js) 323 绘制部件(drawBox()) 326 绘制部件(drawSegments()) 327 着色器着色器程序对象:initShaders() 函数的作用 332 创建着色器对象(gl.createShader()) 333 指定着色器对象的代码(gl.shaderSource()) 334 编译着色器(gl.compileShader()) 334 创建程序对象(gl.createProgram()) 336 为程序对象分配着色器对象(gl.attachShader()) 337 连接程序对象(gl.linkProgram()) 337 告知WebGL 系统所使用的程序对象(gl.useProgram()) 339 initShaders() 函数的内部流程 339 总结 342 第10 章 高级技术 343 用鼠标控制物体旋转 343 如何实现物体旋转 344 示例程序(RotateObject.js) 344 选中物体 347 如何实现选中物体 347 示例程序(PickObject.js) 348 选中一个表面 351 示例程序(PickFace.js) 352 HUD(平视显示器) 355 如何实现HUD 355 示例程序(HUD.html) 356 示例程序(HUD.js) 357 在网页上方显示三维物体 359 雾化(大气效果) 359 如何实现雾化 360 示例程序(Fog.js) 361 使用w 分量(Fog_w.js) 363 绘制圆形的点 364 如何实现圆形的点 364 示例程序(RoundedPoint.js) 366 α 混合 367 如何实现α 混合 367 示例程序(LookAtBlendedTriangles.js) 369 混合函数 369 半透明的三维物体(BlendedCube.js) 371 透明与不透明物体共存 372 切换着色器 373 如何实现切换着色器 374 示例程序(ProgramObject.js) 375 渲染到纹理 379 帧缓冲区对象和渲染缓冲区对象 380 如何实现渲染到纹理 381 示例程序(FramebufferObject.js) 382 创建帧缓冲区对象(gl.createFramebuffer()) 385 创建纹理对象并设置其尺寸和参数 385 创建渲染缓冲区对象(gl.createRenderbuffer()) 386 绑定渲染缓冲区并设置其尺寸(gl.bindRenderbuffer(), gl.renderbufferStorage()) 386 将纹理对象关联到帧缓冲区对象(gl.bindFramebuffer(), gl.framebufferTexture2D()) 388 将渲染缓冲区对象关联到帧缓冲区对象(gl.framebufferRenderbuffer()) 389 检查帧缓冲区的配置(gl.checkFramebufferStatus()) 390 在帧缓冲区进行绘图 390 绘制阴影 392 如何实现阴影 392 示例程序(Shadow.js) 393 提高精度 399 示例程序(Shadow_highp.js) 400 加载三维模型 401 OBJ 文件格式 404 MTL 文件格式 405 示例程序(OBJViewer.js) 406 自定义类型对象 409 示例程序(OBJViewer.js 解析数据部分) 411 响应上下文丢失 418 如何响应上下文丢失 419 示例程序(RotatingTriangle_contextLost.js) 420 总结 422 附录A WebGL 中无须交换缓冲区 423 附录B GLSL ES 1.0 内置函数 427 角度和三角函数 428 指数函数 429 通用函数 430 几何函数 433 矩阵函数 434 矢量函数 435 纹理查询函数 436 附录C 投影矩阵 437 正射投影矩阵 437 透视投影矩阵 437 附录D WebGL/OpenGL :左手还是右手坐标系? 439 示例程序(CoordinateSystem.js) 440 隐藏面消除和裁剪坐标系统 443 裁剪坐标系和可视空间 444 什么是对的? 446 总结 448 附录E 逆转置矩阵 449 附录F 从文件中加载着色器 453 附录G 世界坐标系和本地坐标系 . 457 本地坐标系 458 世界坐标系 459 变换与坐标系 461 附录H WebGL 的浏览器设置 . 463

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值