Neatware Company

An ISV for Mobile, Cloud, and Video Technologies and Software.

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