510 likes | 745 Views
Assembly Shader Language. Chapter 9. What is ASM Shader?. GPU is Graphical Processing Unit of video card .
E N D
Assembly Shader Language Chapter 9
What is ASM Shader? • GPU is Graphical Processing Unit of video card. • ASM Shader is assembly language programming in GPU, Use Shader can do a lot of thing about vertex and pixels. For example we can do dolphin mesh vertices interpolation directly in GPU. • We just pass data and command to GPU and let GPU to finish the work. • One obvious benefit is the Shader programming can greatly enhance the application speed. • The other benefit is that Shader can improve the image quality of the applications. • In following, we will show how to build connecting between application and GPU.
ASM Pixel Shader Pixel Shader is to manage color for every pixel in the rendered 3D object. Suppose that we have three texture pictures, then we can do the following different pixel operations by ASM pixel Shader 1. Show texture1 2. Show texture2, 3. Show texture3 • Mask texture1 by texture3 (Multiply two textures) • Another Mask • Gray color • Assign texture coordinates to color , because texture coords is 2 dimension, we only get red and green colors • Tween of texture 1 and texture 3 • Reverse the texture one (negative image) . All those can be done by very simple ASM Shader code
ASM Pixel Variables ASM Pixel Shader has the following variables: 1. r0, r1, … are temporary variables. If r0 is used as the last calculation result, it is the color output. • c0, c1, … are 4-dim constants, which can be defined either in ASM shade code or get values from application. • Texture data type is tex. Texture colors will be t0, t1. The post-fix number depends the application call like device.setTexture(0, texture0); // this is t0 device.setTexture(1, texture1); // this is t1 However, we must call device.SetTextureStageState(1, TextureStageStates.TextureCoordinateIndex, 0); To enable ASM Shader to get data of t1. • Texture coordinate is texcoord t0
Some Assembly Operations • mul Multiplicationmul z, x, y z = x*y • add Additionadd z, x, y z = x+y • mov Assignmentmul z, x z = x • sub subtractionsub z, x, y z = x-y • m4x4 Matrix Multiplicationmul z, x, M z = M*x • dp3 3 dim dot productadd z, x, y z = x o y
Shader1.psh ;color from texture 1 ps.1.0 ; this pixel shader version tex t0 mov r0,t0
Shader2.psh ;color from texture 2 ps.1.0 tex t1 mov r0,t1
Shader3.psh ;color from texture 3 ps.1.0 tex t2 mov r0,t2
Shader4.psh ;multiply two textures ps.1.0 tex t0 tex t1 mul r0, t0, t1
Shader5.psh ;color from texture1 inverse ps.1.0 tex t0 mov r0, 1-t0
Shader6.psh ps.1.0tex t0tex t2mov r1, 1-t0mul r0, r1, t2mov r0, 1-r0;
Shader7.psh ;get gray format ps.1.0 def c0, 0.33, 0.33, 0.33, 0 tex t0 dp3 r0, t0, c0
Shader8.psh ps.1.0 ;transform texture coords tu, tv into color (r,g,b) ; r=tu, g=tv, b =0 texcoord t0 mov r0, t0
Shader9.psh: c0is from application ps_1_1 tex t0 tex t1 lrp r0, c0, t0, t1
Load psh file to Shader object private PixelShader CreatePixelShader(string strFilename) { GraphicsStream code = ShaderLoader.FromFile(strFilename, null, ShaderFlags.None); return new PixelShader(device, code); } PixelShader shad1 = CreatePixelShader( "shad1.psh");PixelShader shad2 = CreatePixelShader( "shad2.psh"); . . . . . . .PixelShader shad8 = CreatePixelShader( "shad6.psh"); PixelShader shad9 = CreatePixelShader( "shad7.psh");
Keyboard input Control • int shader_flag=0; • protected override void OnKeyDown(KeyEventArgs e) • { • Keys k = e.KeyCode ;if(k==Keys.D1)shader_flag=1;if(k==Keys.D2)shader_flag=2;if(k==Keys.D3)shader_flag=3;if(k==Keys.D4)shader_flag=4;if(k==Keys.D5)shader_flag=5;if(k==Keys.D6)shader_flag=6;if(k==Keys.D7)shader_flag=7;if(k==Keys.D8)shader_flag=8;if(k==Keys.D9)shader_flag=9; • }
Set Shader constents in Render • private void Render(){ . . . . . . . . • if(shader_flag>0)device.SetTexture(0, texture0); • device.SetTexture(1, texture1);device.SetTextureStageState(1, TextureStageStates.TextureCoordinateIndex, 0); • device.SetTexture(2, texture2);device.SetTextureStageState(2, TextureStageStates.TextureCoordinateIndex, 0); • double t= Math.Sin((float)Environment.TickCount/2000f); • float alpha =(float)Math.Pow(t, 2); • Vector4 v= new Vector4( alpha , alpha , alpha ,alpha ); • device.SetPixelShaderConstant(0, v) ;
if(shader_flag==1)device.PixelShader = shad2 • if(shader_flag==2)device.PixelShader = shad2; • if(shader_flag==3)device.PixelShader = shad3 • if(shader_flag==4)device.PixelShader = shad4; • if(shader_flag==5)device.PixelShader = shad5; • if(shader_flag==6)device.PixelShader = shad6; • if(shader_flag==7)device.PixelShader = shad7; • if(shader_flag==8)device.PixelShader = shad8; • if(shader_flag==9)device.PixelShader = shad9;meshCube.DrawSubset(0); • . . . . . . . }
ASM Vertex Shader Vertex Shader is to manage position for every point before drawing. For example, we can use vertex shader to do the same dolphin animation. Because we need to pass multiple vertices data to Shader, we need use VertexElement to define VertexDeclararion object, which make possible to enable shader to know the vertex input data from application.
Structure VertexElement public VertexElement(short stream,short offset, DeclarationType declType, DeclarationMethod declMethod, DeclarationUsage declUsage,byte usageIndex);
VertexElement DeclarationType The following table are some DeclarationType samples
VertexElementDeclarationUsage The following table are some DeclarationUsage samples
SetVertexShaderConstant Method device.SetVertexShaderConstant(…) is to pass data to GPU Shader. It has the following formats Note1: The first integer argument will determine the data name in GPU Shader Note 2: Any input data beginning with letter 'c'.
All matrices must be passed too All matrices, including Viewpoint matrix, Projective matrix and any matrix that determines the position of 3D object must be passed to Shader too. However, any matrix must be transposed before calling SetVertexShaderConstant() function. We no longer need to call the followings: device.Transform.View = MatrixView; device.Transform.Projection = MatrixProjective; device.Transform.World = MatrixPosition; Actually they are disabled by Shader operations
Initialize Shader Operation Load Shader code from vertex shader file with extension *.vsh GraphicsStream stream =ShaderLoader.FromFile( "DolphinTween.vsh", null, ShaderFlags.None); VertexShader dolphinVertexShader = new VertexShader(device, stream) Then in Render() function, we call device.VertexDeclaration = dolphinVertexDeclaration;device.VertexShader = dolphinVertexShader; // pass codeSetVShaderParameters(); // pass data Note: We still need to call functions like: device.SetStreamSource(…);device.Indices = dolphinIndexBuffer;device.DrawIndexedPrimitives(…)
Procedure of Shader Application • Define VertexElement array. • Setup VertexDeclaration. • Load Shader code from vsh file, create shader object • Create all VertexBuffer that can recognized by Shader • Crate all other data including matrices for passing. • SetShaderConstants, pass data to Shader • Call device.VertexShader = Shader Object;
Redesign Rotation Triangle VertexDeclaration decl;VertexShader vShader ; new VertexElement[] velements = new VertexElement[] { new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0), new VertexElement(0, 12, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0), VertexElement.VertexDeclarationEnd }; decl = new VertexDeclaration(device, velements) vShader = new VertexShader(device, ShaderLoader.FromFile( "rotation.vsh", null, ShaderFlags.None));
Set Vertex Shader Value • private void SetViewProjectPosition(){ • Matrix matView = Matrix.LookAtLH( new Vector3( 0.0f, 2f, -5.0f ), new Vector3( 0.0f, 0.0f, 0.0f ), new Vector3( 0.0f, 1.0f, 0.0f ) ); Matrix matProj = Matrix.PerspectiveFovLH( (float)Math.PI / 4.0f, (float)this.Width /(float)this.Height, 1.0f, 1000.0f ); float t = (float)Environment.TickCount/500f *Math.PI; Matrix matRot= Matrix.RotationY(t); Matrix worldViewProj = matRot * matView* matProj ; • Matrix mat = Matrix.TransposeMatrix(worldViewProj); • device.SetVertexShaderConstant(4, mat); • } Note: In shader, matrix mat will be c4
In Render • private void Render(){ • SetViewProjectPosition() ; • device.Clear(D3D.ClearFlags.Target|ClearFlags.ZBuffer, Color.Blue.ToArgb (), 1.0f,1); • device.BeginScene(); • device.RenderState.CullMode = Cull.None ; • device.VertexDeclaration = decl; • device.VertexShader =vShader; • device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); • device.EndScene(); • Device.Present(); • }
ASM code ;---------------------------------------------------------; Constants specified by the app; c4 = matWorldViewProjection;---------------------------------------------------------vs.1.1 dcl_position0 v0dcl_color0 v1 ; rotation mov r0, v0m4x4 oPos, r0, c4 ; color mov oD0, v1
Redesign Dolphin Animation We have three given Dolphin meshes. In the last chapter, we directly to do the mesh interpolation. Now we want design Shader operation to do the exactly same Dolphin mesh interpolation.
Some member variables Device device; Matrix worldMatrix = Matrix.Identity;Matrix viewMatrix = Matrix.Identity; Matrix projectionMatrix = Matrix.Identity; Material dolphinMtrl;VertexBuffer dolphinVertexBuffer1 = null;VertexBuffer dolphinVertexBuffer2 = null; VertexBuffer dolphinVertexBuffer3 = null;IndexBuffer dolphinIndexBuffer = null; int numDolphinVertices = 0;int numDolphinFaces = 0; VertexDeclaration dolphinVertexDeclaration = null; VertexShader dolphinVertexShader = null;Vector3 vLight = new Vector3(0, -1, 0);
Vertex Structure • public struct Vertex // preparing for shader • { • public Vector3 p; • public Vector3 n; • public float tu, tv; • public static readonly VertexFormats Format = • VertexFormats.Position | • VertexFormats.Normal | • VertexFormats.Texture1; • };
Set VertexDeclaration • private void SeVShaderDeclaration() • { • VertexElement[] dolphinVertexDecl = new VertexElement[] {//the First stream is the first dolphin mesh • newVertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0), • newVertexElement(0, 12, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Normal, 0), • new VertexElement(0, 24, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0), • // the second stream is the 2nd dolphin mesh • new VertexElement(1, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 1),
new VertexElement(1, 12, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Normal, 1), • new VertexElement(1, 24, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 1), • // Third stream is the 3rd dolphin mesh • new VertexElement(2, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 2), • new VertexElement(2, 12, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Normal, 2), • new VertexElement(2, 24, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 2), • VertexElement.VertexDeclarationEnd }; • dolphinVertexShader = new VertexDeclaration(device, dolphinVertexDecl); • }
private void SetDolphinVertexBuffer(){ • Mesh dolphinMesh1, dolphinMesh2, dolphinMesh3;ExtendedMaterial [] exMtrl;try{ • dolphinMesh1 = Mesh.FromFile("dolphin1.x", MeshFlags.Managed, device, out exMtrl); • dolphinMtrl = exMtrl[0].Material3D; • dolphinMesh2 = Mesh.FromFile("dolphin2.x", MeshFlags.Managed, device); • dolphinMesh3 = Mesh.FromFile("dolphin3.x", MeshFlags.Managed, device); • } • catch(Exception) • { MessageBox.Show("Could not load X files"); return; }
numDolphinVertices = dolphinMesh1.NumberVertices ;numDolphinFaces = dolphinMesh1.NumberFaces ; • // create 3 empty VertexBuffer • dolphinVertexBuffer1 = new VertexBuffer(typeof(Vertex),numDolphinVertices, device, Usage.WriteOnly, 0, Pool.Managed); • dolphinVertexBuffer2 = new VertexBuffer(typeof(Vertex),numDolphinVertices, device, Usage.WriteOnly, 0, Pool.Managed); • dolphinVertexBuffer3 = new VertexBuffer(typeof(Vertex), numDolphinVertices, device, Usage.WriteOnly, 0, Pool.Managed); • // create one empty IndexBuffer • dolphinIndexBuffer = new IndexBuffer(typeof(short), numDolphinFaces * 3, device, Usage.WriteOnly, Pool.Managed); • VertexBuffer pMeshSourceVB = null;IndexBuffer pMeshSourceIB = null; • CustomVertex.PositionNormal[] src = null; • Vertex[] dst = null;
// Copy vertices for mesh 01pMeshSourceVB = dolphinMesh1.VertexBuffer; • dst = (Vertex[])dolphinVertexBuffer1.Lock(0, typeof(Vertex), 0, numDolphinVertices); • src = (CustomVertex.PositionNormal[])pMeshSourceVB.Lock(0, typeof(CustomVertex.PositionNormal), 0, numDolphinVertices); • for(int k=0; k<numDolphinVertices; k++){ dst[k].p = src[k].Position; dst[k].n = src[k].Normal;}dolphinVertexBuffer1.Unlock();pMeshSourceVB.Unlock();pMeshSourceVB.Dispose(); • // Copy vertices for mesh 02 • MeshSourceVB = dolphinMesh2.VertexBuffer;dst = (Vertex[])dolphinVertexBuffer2.Lock(0, typeof(Vertex), 0, numDolphinVertices);src = (CustomVertex.PositionNormal[])pMeshSourceVB.Lock(0, typeof(CustomVertex.PositionNormal), 0, numDolphinVertices);
for(int k=0; k<numDolphinVertices; k++){ dst[k].p = src[k].Position; dst[k].n = src[k].Normal;} dolphinVertexBuffer2.Unlock();pMeshSourceVB.Unlock();pMeshSourceVB.Dispose(); • // Copy vertices for mesh 03 • pMeshSourceVB = dolphinMesh3.VertexBuffer;dst = (Vertex[])dolphinVertexBuffer3.Lock(0, typeof(Vertex), 0, numDolphinVertices); • src = (CustomVertex.PositionNormal[])pMeshSourceVB.Lock(0, typeof(CustomVertex.PositionNormal), 0, numDolphinVertices); • for(int k=0; k<numDolphinVertices; k++){ dst[k].p = src[k].Position; dst[k].n = src[k].Normal;}
dolphinVertexBuffer3.Unlock(); • pMeshSourceVB.Unlock(); • pMeshSourceVB.Dispose(); • // Copy indices for the dolphin mesh • pMeshSourceIB = dolphinMesh1.IndexBuffer; • short[] indices = (short[]) pMeshSourceIB.Lock(0, typeof(short), 0, numDolphinFaces * 3); • dolphinIndexBuffer.SetData(indices, 0, LockFlags.None); • pMeshSourceIB.Unlock();pMeshSourceIB.Dispose(); • dolphinVertexShader = new VertexShader(device, ShaderLoader.FromFile( "DolphinTween.vsh", null, ShaderFlags.None) • ); • }
private void SetVShaderParameters() // pass data to GPU{ • float t = (float)Environment.TickCount/3000f; float blendWeight = (float)Math.Sin(6*t); float weight1, weight2,weight3; • if (blendWeight > 0.0f) { • weight1 = (float)Math.Abs(blendWeight); weight2 = 1.0f - (float)Math.Abs(blendWeight); weight3 = 0.0f;}else{ weight1 = 0.0f; weight2 = 1.0f - (float)Math.Abs(blendWeight); weight3 = (float)Math.Abs(blendWeight);}Vector4 vWeight = newVector4(weight1, weight2, weight3, 0.0f);
Vector4 fLight = new Vector4(0.0f, 1.0f, 0.0f, 0.0f); • Vector4 fLightDolphinSpace = new Vector4(0.0f, 1.0f, 0.0f, 0.0f); • float[] fDiffuse = { 1.00f, 1.00f, 1.00f, 1.00f }; • float[] fAmbient = { 0.25f, 0.25f, 0.25f, 0.25f }; • Matrix S = Matrix.Scaling(0.01f, 0.01f, 0.01f); • Matrix RZ = Matrix.RotationZ(-(float)Math.Cos(6*t)/6); • Matrix RY = Matrix.RotationY(t);Matrix T = Matrix.Translation(-5*(float)Math.Sin(t), • (float)Math.Sin(6*t)/2, 10-10*(float)Math.Cos(t)); • Matrix matDolphin = S*RZ*RY*T; • Matrix matDolphinInv = Matrix.Invert(matDolphin); • fLightDolphinSpace = Vector4.Transform(fLight, matDolphinInv); • fLightDolphinSpace.Normalize();
Matrix mat = matDolphin * viewMatrix * projectionMatrix;Matrix matTranspose= Matrix.TransposeMatrix(mat);Matrix matCamera = matDolphin * viewMatrix;Matrix matCameraTranspose = Matrix.TransposeMatrix(matCamera);Matrix matViewTranspose= Matrix.TransposeMatrix(viewMatrix);Matrix matProjTranspose= Matrix.TransposeMatrix(projectionMatrix); Vector4 vZero = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);Vector4 vOne = new Vector4(1.0f, 0.5f, 0.2f, 0.05f); // Set the vertex shader constants device.SetVertexShaderConstant(0, vZero );device.SetVertexShaderConstant(1, vOne );device.SetVertexShaderConstant(2, vWeight);device.SetVertexShaderConstant(4, matTranspose ) ;device.SetVertexShaderConstant(8, matCameraTranspose); device.SetVertexShaderConstant(12, matViewTranspose );device.SetVertexShaderConstant(19, fLightDolphinSpace );device.SetVertexShaderConstant(20, fLight );device.SetVertexShaderConstant(21, fDiffuse);device.SetVertexShaderConstant(22, fAmbient); }
Constants specified by the last function ; c0 = ( 0, 0, 0, 0 ); c1 = ( 1, 0.5, 2, 4 ); c2 = ( weight1, weight2, weight3, 0 ); c4-c7 = matWorldViewProjection; c8-c11 = matWorldView; c19 = light direction (in model space); c21 = material diffuse color * light diffuse color; c22 = material ambient color; Actually they are memory address Vertex components (as specifiedin the vertex DECL) ; v0 = Position; v3 = Normal; v6 = Texcoords
Initializing • public void StartGame(){ . . . . . . . • SetDolphinVertexBuffer(); • SeVShaderDeclaration(); • while(GameActive) • { • Render(); • Application.DoEvents(); • } • }
Calling Shader • public void Render(){ . . . . . . . • device.VertexDeclaration = dolphinVertexDeclaration; • device.VertexShader = dolphinVertexShader; • SetVShaderParameters() ; • device.SetStreamSource(0, dolphinVertexBuffer1, 0); • device.SetStreamSource(1, dolphinVertexBuffer2, 0); • device.SetStreamSource(2, dolphinVertexBuffer3, 0); • device.Indices = dolphinIndexBuffer; • device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, numDolphinVertices,0, this.numDolphinFaces); . . . . . . . .. • }
Shader Built-in Output Variables They are called built-in registers, beginning with letter ‘o’ Input variables in Shader have names begin with letter ‘c’. Free variables in Shader have names begin with letter ‘r’. We also can declare variables begin with letter ‘v’ .
Dolphin Vertex Shader file vs.1.1 ; vertex shader version dcl_position0 v0dcl_position1 v1dcl_position2 v2dcl_normal1 v4dcl_normal2 v5dcl_texcoord0 v6 ; Tween the 3 positions (v0,v1,v2) into one position mul r0, v0, c2.xmul r1, v1, c2.ymul r2, v2, c2.zadd r3, r0, r1add r3, r3, r2 ; Transform position to the clipping space m4x4 oPos, r3, c4
; Lighting calculation;Tween the 3 normals (v3,v4,v5) into one normal mul r0, v3, c2.xmul r1, v4, c2.ymul r2, v5, c2.zadd r3, r0, r1add r3, r3, r2 ; Do the lighting calculation dp3 r1.x, r3, c19 ; r1 = normal dot lightmax r1.x, r1.x, c0.x ; if dot < 0 then dot = 0mul r0, r1.x, c21 ; Multiply with diffuseadd r0, r0, c22 ; Add in ambientmin oD0, r0, c1.x ; clamp if > 1 ;Texture coordinates ; Gen tex coords from vertex xz position mul oT0.xy, c1.y, r9.xz