Ripple
This program generates a ripple effect to a picture on a plane. Cg vertex program is used to simulate the
wave on the surface of water. Cg pixel program simply render the vertices.
This program demonstrates the use of Cg and the use of vertex, index buffers with MCL.
package require mcl
it loads mcl package with latest version.
MCL::procedure rippleVertex {
__internal float dot( float2 a, float2 b );
__internal float dot( float3 a, float3 b );
__internal float dot( float4 a, float4 b );
ripple vertex shader with MCL::procedure for Cg. The Cg source code is exactly copied in the body of the rippleVertex procedure.
struct appdata {
float3 position : POSITION;
float3 normal : NORMAL;
float2 tex1 : TEXCOORD0;
};
defines vertex position with type float3 and binding to POSITION;
normal with type float3 and binding NORMAL;
tex1 with type float2 and binding TEXCOORD0.
struct vfconn {
float4 Position : POSITION;
float4 Color : COLOR0;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
it defines vertex to pixel structure with Position, Color, and texcoord0/texcoord1.
vfconn main(appdata I,
uniform float4x4 mat,
uniform float4 vA,
uniform float4 vD,
uniform float4 vSin,
uniform float4 vCos,
uniform float4 Kd)
{
vfconn O;
this Cg procedure defines vertex shader code.
mat - View+Projection matrix
vA.xyzw - time, 0, 0.5, 1.0
vD.xyzw - pi, 1/2pi, 2pi, 0.05
vSin - first 4 taylor coefficients for sin(x)
vCos - first 4 taylor coefficients for cos(x)
float4 v = float4(I.position.x, 1, I.position.y, 1);
float4 r4 = float4(v.x, 0, v.z, 0);
float4 one = float4(1, 1, 1, 1);
float d = sqrt(dot(r4, r4)) * vA.x;
convert I.position to float4 from float3. sqrt(dot(x, x)) gets the length of vector x.
// Clamp theta to -pi..pi
d = (d + vD.x) * vD.y;
d = frac(d);
d = (d * vD.z) - vD.x;
float d2, d4, d6;
d2 = d * d;
d4 = d2 * d2;
d6 = d4 * d2;
float4 r5 = float4(1.0, d2, d4, d6);
// r4 = d, d3, d5, d7
r4 = r5 * float4(d,d,d,d);
r4 = r4 * vSin;
float s = dot(r4, one);
r5 = r5 * vCos;
float c = dot(r5, one); // sum 4 terms
generate coffercients
// Set color
d = (1.0 - c) * 0.5;
O.Color = Kd * d;
// Scale height
v.y = s * vD.w;
O.Position = mul(mat, v);
float4 normal = normalize(mul(mat,
float4(I.normal.x, I.normal.y, I.normal.z, 0.0)));
O.texcoord0 = normal.xy * s; // float(s, c);
O.texcoord1 = normal.xy;
// O.texcoord1 = I.texcoord0 * s;
return O;
}
}
generate position, normal, and texture coordinates
#
# ripple pixel shader
#
MCL::procedure ripplePixel {
struct v2f_simple {
float4 Position : POSITION;
float4 Color : COLOR0;
float4 texcoord0 : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
};
defines ripple pixel procedure and vertex to pixel structure.
fragout main(v2f_simple I,
uniform sampler2D tex0,
uniform sampler2D tex1,
uniform float height)
{
fragout O;
specefies the pixel procedure with input structure I, sampler2D tex0 and tex1 for textures, and height for scale value.
//fetch base color
float4 color = tex2D(tex0);
//fetch bump normal
// float4 bumpNormal = expand(tex2D(tex1));
//O.col = uclamp(dot3_rgba(bumpNormal.xyz, color.xyz))
// * I.Color * height;
O.col = I.Color * height;
return O;
}
}
pixel source code.
proc setInitBinding {} {
global app mat vt pl PI freq
# matrix
matrix m
MatrixMultiply $m $mat(View) $mat(Projector)
MatrixTranspose $m $m
set initial ViewProjection matrix
set fTime [expr sin([$app getTime])*freq]
float vA [list $fTime 0.0 0.5 1.0]
float4 vD [list $PI [expr 0.5/PI] [expr 2.0*PI] 0.05]
float4 vSin [list 1.0 [expr -1.0/6.0] \\
[expr 1.0/120.0] \\
[expr -1.0/5040.0]]
float4 vCos [list 1.0 [expr -0.5] \\
[expr 1.0/24.0] \\
[expr -1.0/720.0]]
float4 Kd [list 1.0 1.0 1.0 0.5]
float height 2.0
set initial bind values
$vt const $m $vA $vD $vSin $vCos $Kd
$pl const $height
}
set vertex and pixel shader constants
proc InitStartup {} {
global mat
# vEye vAt vUp
float3 vEye {1.0 3.0 3.0}
float3 vAt {0.0 0.0 0.0}
float3 vUp {0.0 1.0 0.0}
float det 1.0
MatrixLookAtLH $mat(View) $vEye $vAt $vUp
MatrixInverse $mat(Position) $det $mat(View)
}
one time init scene, init view and position matrix with vEye, vAt, and vUp vectors.
proc FrameMove {} {
global app mat vfillmode
global PI velocity angVelocity
global fSpeed fAngularSpeed
one frame processing
float3 vT {0.0 0.0 0.0}
float3 vR {0.0 0.0 0.0}
# check keyboard input
if {[$app keyState VK_LEFT] || [$app keyState VK_NUMPAD1]} {
Vec3X-= $vT 0.50}
if {[$app keyState VK_RIGHT] || [$app keyState VK_NUMPAD3]} {
Vec3X+= $vT 0.50}
if {[$app keyState VK_DOWN]} {Vec3Y-= $vT 0.50}
if {[$app keyState VK_UP]} {Vec3Y+= $vT 0.50}
if {[$app keyState W]} {Vec3Z-= $vT 0.25}
if {[$app keyState S]} {Vec3Z+= $vT 0.25}
if {[$app keyState A] || [$app keyState VK_NUMPAD8]} {
Vec3X-= $vR 0.50}
if {[$app keyState Z] || [$app keyState VK_NUMPAD2]} {
Vec3X+= $vR 0.50}
if {[$app keyState E] || [$app keyState VK_NUMPAD6]} {
Vec3Y-= $vR 0.50}
if {[$app keyState Q] || [$app keyState VK_NUMPAD4]} {
Vec3Y+= $vR 0.50}
if {[$app keyState VK_NUMPAD9]} {Vec3Z-= $vR 1.0}
if {[$app keyState VK_NUMPAD7]} {Vec3Z+= $vR 1.0}
if {[$app keyState X]} {Vec3X-= $LightPos 0.01}
if {[$app keyState C]} {Vec3X+= $LightPos 0.01}
if {[$app keyState Y]} {Vec3Y-= $LightPos 0.01}
if {[$app keyState U]} {Vec3Y+= $LightPos 0.01}
if {[$app keyState F]} {
if {[string compare $vfillmode wireframe] == 0} {
set vfillmode solid
} else {
set vfillmode wireframe
}
$app setRenderState fillmode $vfillmode
}
check keyboard input for interactive control.
Vec3Scale $velocity $velocity 0.9
Vec3Scale $vT $vT 0.1
Vec3Add $velocity $velocity $vT
set velocity
Vec3Scale $angVelocity $angVelocity 0.9
Vec3Scale $vR $vR 0.1
Vec3Add $angVelocity $angVelocity $vR
set angular velocity
set rate [$app getElapsedTime]
Vec3Scale $vT $velocity [expr rate * fSpeed]
Vec3Scale $vR $angVelocity [expr rate * fAngularSpeed]
set vT and vR
matrix matT
MatrixTranslation $matT [Vec3GetX $vT] \\
[Vec3GetY $vT] \\
[Vec3GetZ $vT]
MatrixMultiply $mat(Position) $matT $mat(Position)
set position
matrix matR
quaternion qR {0.0 0.0 0.0 0.0}
QuaternionRotationYawPitchRoll $qR \\
[Vec3GetY $vR] \\
[Vec3GetX $vR] \\
[Vec3GetZ $vR]
MatrixRotationQuaternion $matR $qR
set matR for rotation
MatrixMultiply $mat(Position) $matR $mat(Position)
MatrixInverse $mat(View) 0.0 $mat(Position)
$app setTransform view $mat(View)
setInitBinding
}
set transform view and initializes binding
proc ShowHelp {} {
global app ft
# show help
if {[$app showHelp]} {
set cmdTitle "Keyboard Controls:"
set cmdOp "Play\nStep\nMove\nTurn\nPitch\n
Slider\nRotation\nWireframe\nHelp\n
Play\nRestart\nFullscreen\nExit"
set cmdKey "Enter\nSpace\nW,S\nE,Q\nA,Z\nArrow keys\n
Home,PageUp\nF\nF1\nF2\nF4\nF5\nEsc"
when F1 key is pressed prompt the help message.
# draw help
$ft(1) draw 2 40 0xFFFFFFFF $cmdTitle
$ft(1) draw 20 60 0xFFFFFFFF $cmdOp
$ft(1) draw 210 60 0xFF00FFFF $cmdKey
# draw stats
$ft(0) draw 2 0 0xff00ff00 [$app getFrameStats]
$ft(0) draw 2 20 0xff00ff00 [$app getDeviceStats]
}
}
draw help and status message
#
# render
# render a frame
#
proc Render {} {
global app vt pl tx L vertice
global ft vb ib
# clear flags color z stencil with color ARGB
$app clear {target zbuffer} 0x00004040 1.0 0
$app beginScene
# show help
ShowHelp
render a frame in the Render function. [$app clear ...] clears the scene. [$app beginScene] starts the scene.
$tx set 0
$vt active
$pl active
set texture and active vertex/pixel shaders.
# render vbuffer
$vb stream 0 [$vertice size]
$ib indices 0
$app drawIndexedPrimitive trianglelist \\
0 [expr L*L] 0 [expr (L-1)*(L-1)*6/3]
$app endScene
}
draw indexed primitive with triangle list by setting the stream of vertex buffer and indices of index buffer.
proc InitDeviceObjects {} {
global ft
$ft(0) init
$ft(1) init
}
InitDeviceObjects with font initialization.
proc RestoreDeviceObjects {} {
global app ft
$app setTextureStageState 0 magfilter linear
$app setTextureStageState 0 minfilter linear
restore device and objects by setting the texture stage state and others.
# set states
$app setRenderState lighting false
$app setRenderState cullmode none
$app setRenderState alphablendenable true
$app setRenderState srcblend srccolor
$app setRenderState destblend one
set render states. lighting is disabled without using the fixed-function lighting.
shader
projector
vbuffer
ibuffer
restore shader, vbuffer, and ibuffer
$ft(0) restore
$ft(1) restore
}
restore font objects
proc InvalidateDeviceObjects {} {
global ft ib vb
global tx pl vt sh
# font
$ft(1) invalidate
$ft(0) invalidate
invalidate font objects
# buffers
$ib release
$vb release
release index and vertex buffers.
# shaders
$tx release
$pl release
$vt release
$sh release
}
release texture, pixel, vertex, and shader objects.
proc DeleteDeviceObjects {} {
global ft
$ft(1) delete
$ft(0) delete
}
delete font objects
proc FinalCleanup {} {
global ft
$ft(1) release
$ft(0) release
}
finally release font objects
proc ibuffer {} {
global app ib
global PI L
# $app ibuffer indices usage index pool
set ib [$app ibuffer [expr (L-1)*(L-1)*6] \\
writeonly index16 managed]
create index buffer ib
$ib lock
set n 0
for {set y 1} {y < L} {incr y} {
for {set x 1} {x < L} {incr x} {
$ib setIndices [expr n+0] [expr (y-1)*L + (x-1)]
$ib setIndices [expr n+1] [expr (y-0)*L + (x-1)]
$ib setIndices [expr n+2] [expr (y-1)*L + (x-0)]
$ib setIndices [expr n+3] [expr (y-1)*L + (x-0)]
$ib setIndices [expr n+4] [expr (y-0)*L + (x-1)]
$ib setIndices [expr n+5] [expr (y-0)*L + (x-0)]
incr n 6
}
}
lock index buffer ib. set index items.
$ib unlock
}
unlock index buffer
proc vbuffer {} {
global app vb vertice
global PI L
# create vbuffer
# $app vbuffer nvertices vsize usage fvf pool
set vb [$app vbuffer [expr L*L] [$vertice size] \\
writeonly 0 managed]
create vertex buffer
$vb lock
lock vertex buffer
set L1 [expr 1.0/(L-1)]
$vertice init [$vb getBuffer]
init vertice and point to vb buffer.
for {set y 0} {y < [expr L]} {incr y} {
for {set x 0} {x < [expr L]} {incr x} {
# set items
$vertice set position \\
[list [expr (x*L1 - 0.5)*PI] \\
[expr (y*L1 - 0.5)*PI] 1.0]
$vertice set normal [list [expr x*L1] \\
[expr y*L1] 0.0]
$vertice set tex1 [list [expr x*L1] \\
[expr y*L1]]
$vertice next 1
}
}
set position, normal, and texture coordiate of the vertex buffer.
$vb unlock
}
unlock vertex buffer
proc shader {} {
global app sh vt pl tx
set sh [$app shader cg ..]
create a Cg shader with path ..
set vDecl [list [list 3 position 0] \\
[list 3 normal 0] \\
[list 2 tex1 0]]
set vt [$sh vertex "Ripple Vertex" \\
[rippleVertex] cgDX8VertexProfile $vDecl]
$vt bind {mat vA vD vSin vCos Kd}
create vertex shader with name "Ripple Vertex", Cg code defined in rippleVertex procedure, vertex profile of DX8, and vertex declaration vDecl. vt is binded to the parameters of the Cg vertex procedure.
set pl [$sh pixel "Ripple Pixel" \\
[ripplePixel] cgDX8PixelProfile]
$pl bind {height}
create a pixel shader with name "Ripple Pixel", Cg code defined in ripplePixel, and pixel profile of DX8. pl is bindec to the parametes of the Cg pixel procedure.
set tx [$pl texture [$app dialog file *.jpg [getTexture]]]
$tx bind {tex0}
}
create texture for pixel shader. [$app dialog file name folder] opens a file dialog with initial file
name and folder.
proc projector {} {
global app mat PI
float fAspectRatio [expr double([$app getBackBufferWidth]) \\
/ double([$app getBackBufferHeight])]
MatrixPerspectiveFovLH $mat(Projector) \\
60 $fAspectRatio 0.1 100.0
$app setTransform projection $mat(Projector)
}
set projection transform
proc curpath {} {
return [file join [pwd] [file dirname [info script]]]
}
return current script path
proc getTexture {{fname ""}} {
return [file join [curpath] .. .. .. media texture $fname]
}
return texture path that stores many texture files.
proc main {} {
global app ft vertice
# start application
set app [MCL::application]
main is the starting function of MCL. [MCL::application] command creates an application object.
# create text font
set ft(0) [$app font Vertana 10 0]
set ft(1) [$app font Arial 8 0]
create text font object array with [$app font name size style].
# create vertice struct decl
set vertice [MCL::struct {float3 position
float3 normal float2 tex1}]
create vertice with [MCL::struct elemlist] command.
# run
$app create
$app run
}
create application and run.
set PI 3.1415926535897932384626
set L 32
set freq 5
set vfillmode solid
set global constants. try possible freq value 1, 10, 100, 1000 ... to see the effects.
float fSpeed 5.0
float fAngularSpeed 1.0
float3 velocity {0.0 0.0 0.0}
float3 angVelocity {0.0 0.0 0.0}
matrix mat(Position)
matrix mat(View)
matrix mat(Projector)
set global variables
main
exit 0
start program
|