PS2 Vertex Shader
Year 2

Using the Playstation 2 Linux development kit, we created a number of projects, culminating in a 3D program. The aim was to create a program that would use the available hardware of the Playstation 2 in a suitable manner, taking into account the powerful parallel rendering architecture.

My application (named "GunnAPI") is an extension of the application framework by Dr. H. Fortuna. The API is expanded in two areas: lighting and sound. Expanding on the ambient and directional lights in the initial framework, GunnAPI supports point lights. This means that the framework can render 3D scenes with up to 7 different lights in total. It also integrates the Linux OpenSound application framework to provide very basic audio support for games.

Screenshot 1

Three point lights (green, purple and blue) with test model.

Screenshot 2

Another three point lights, where the blue light is clearly illuminating two models.

The full source code, including makefile, is available HERE, however the main changes to support point lights (and the bulk of the project work) are found in the VU code:

/* ;* PS2 Application Framework ;* University of Abertay Dundee ;* May be used for educational purposed only ;* Author - Dr Henry S Fortuna ;* Modified by Shaun Pattillo ;* Revision: 1.7 (Specular Branch) ;* Date: 25/11/2007 ;*/ ; The static (or initialisation buffer) i.e. stuff that doesn't change for each ; time this code is called. Scales .assign 0 ; 0 | Scale | Vector LightDirs .assign 1 ; 1-4 | Directional Light Directions | 4x4 Matrix LightCols .assign 5 ; 5-8 | Directional/Ambient Colour | 4x4 Matrix Transform .assign 9 ; 9-12 | Transform | 4x4 Matrix LightTrans .assign 13 ; 13-16 | World Transform | 4x4 Matrix PointLPos .assign 17 ; 17-20 | Point Light Positions | 4x4 Matrix PointLCols .assign 21 ; 21-24 | Point Light Colours | Light4x4 Matrix CameraPos .assign 25 ; 25 | Camera Position | Vector ; The input buffer (relative the the start of one of the double buffers) NumVerts .assign 0 GifPacket .assign 1 UVStart .assign 2 NormStart .assign 3 VertStart .assign 4 ; The output buffer (relative the the start of one of the double buffers) GifPacketOut .assign 248 UVStartOut .assign 249 NormStartOut .assign 250 VertStartOut .assign 251 ; Note that we have 4 data buffers: InputBuffer0, OutputBuffer0, InputBuffer1, and OutputBuffer1. ; Each buffer is 248 quad words. The different buffers are selected by reading the current offset ; from xtop (which is swapped automatically by the PS2 after each MSCALL / MSCNT). .include "vcl_sml.i" .init_vf_all .init_vi_all .syntax new .vu --enter --endenter ; The START or Init code, that is only called once per frame. START: fcset 0x000000 lq fScales, Scales(vi00) fLightDirs[0], LightDirs+0(vi00) ; Load the light directions fLightDirs[1], LightDirs+1(vi00) fLightDirs[2], LightDirs+2(vi00) fLightCols[0], LightCols+0(vi00) ; Load the light colours fLightCols[1], LightCols+1(vi00) fLightCols[2], LightCols+2(vi00) fAmbient, LightCols+3(vi00) MatrixLoad fTransform, Transform, vi00 MatrixLoad fLightTrans, LightTrans, vi00 MatrixLoad fPointLPos, PointLPos, vi00 lq fCameraPos, CameraPos(vi00) ; Load the current camera position ; This begin code is called once per batch begin: xtop iDBOffset ; Load the address of the current buffer (will either be QW 16 or QW 520) ilw.x iNumVerts, NumVerts(iDBOffset) iadd iNumVerts, iNumVerts, iDBOffset iadd Counter, vi00, iDBOffset loop: ; ***** VERTEX TRANSFORM ***** lq fVert, VertStart(Counter) ; Load the vertex from the input buffer MatrixMultiplyVertex Vert, fTransform, fVert ; Transform the vertex into world(clip) space Vert, Vert ; Clip it fcand vi01, 0x3FFFF iaddiu iADC, vi01, 0x7FFF ilw.w iNoDraw, UVStart(Counter) ; Load the iNoDraw flag. If true we should set the iadd iADC, iADC, iNoDraw ; ADC bit so the vert isn't drawn isw.w iADC, VertStartOut(Counter) div q, vf00[w], Vert[w] ; ***** TEXTURE MAPPING ***** lq UV, UVStart(Counter) ; Handle the tex-coords mul UV, UV, q sq UV, UVStartOut(Counter) Vert, Vert, q ; Scale the final vertex to fit to the screen. acc, fScales, vf00[w] Vert, Vert, fScales Vert, Vert Vert, VertStartOut(Counter) ; And store in the output buffer ; ***** LIGHTING ***** MatrixMultiplyVertex fVert, fLightTrans, fVert ; Transform the vertex into world space lq Norm, NormStart(Counter) ; Load the normal MatrixMultiplyVertex Norm, fLightTrans, Norm ; Transform the normal into world space (Important that norm.w is 0) ; ***** POINT LIGHTS ***** ToLight[0], fPointLPos[0], fVert ; Get the vector from the vert to the point lights ToLight[1], fPointLPos[1], fVert ToLight[2], fPointLPos[2], fVert VectorNormalizeXYZ ToLight[0], ToLight[0] ; Normalise this vector VectorNormalizeXYZ ToLight[1], ToLight[1] ; (and it becomes the light direction, just as in directional lighting) VectorNormalizeXYZ ToLight[2], ToLight[2] VectorDotProduct fPointIntensity[0], ToLight[0], Norm ; Calcualate the intensity based on normals. VectorDotProduct fPointIntensity[1], ToLight[1], Norm VectorDotProduct fPointIntensity[2], ToLight[2], Norm ; ***** SPECULAR ***** lq fCameraPos, CameraPos(vi00) ; Load the current camera position MatrixMultiplyVertex fCameraPos, fLightTrans, fCameraPos fToCam, fCameraPos, fVert ; Get the vector from vertex to camera VectorNormalizeXYZ fToCam, fToCam ; And normalize it HalfVec[0], fToCam, ToLight[0] HalfVec[1], fToCam, ToLight[1] HalfVec[2], fToCam, ToLight[2] VectorNormalizeXYZ HalfVec[0], HalfVec[0] VectorNormalizeXYZ HalfVec[1], HalfVec[1] VectorNormalizeXYZ HalfVec[2], HalfVec[2] VectorDotProduct fSpecIntensity[0], HalfVec[0], Norm VectorDotProduct fSpecIntensity[1], HalfVec[1], Norm VectorDotProduct fSpecIntensity[2], HalfVec[2], Norm POWER32 fSpecIntensity, fPointIntensity ;macro'd (power 32 fSpec that add it to fPoint) ; ***** POINT LIGHTS ***** max.x fPointIntensity[0], fPointIntensity[0], vf00 ; Clamp to > 0 mini.x fPointIntensity[0], fPointIntensity[0], vf00[w] ; Clamp to < 1 max.x fPointIntensity[1], fPointIntensity[1], vf00 ; Clamp to > 0 mini.x fPointIntensity[1], fPointIntensity[1], vf00[w] ; Clamp to < 1 max.x fPointIntensity[2], fPointIntensity[2], vf00 ; Clamp to > 0 mini.x fPointIntensity[2], fPointIntensity[2], vf00[w] ; Clamp to < 1 MatrixLoad fPointLCols, PointLCols, vi00 ; Load the light colour acc, fPointLCols[0], fPointIntensity[0][x] ; Scale the colour by the intensity acc, fPointLCols[1], fPointIntensity[1][x] fPointCols, fPointLCols[2], fPointIntensity[2][x] loi 128 ; Load 128 to I addi.w fPointCols, vf00, i ; ***** DIRECTIONAL LIGHTS ***** acc, fLightDirs[0], Norm[x] ; "Transform" the normal by the light direction matrix acc, fLightDirs[1], Norm[y] ; This has the effect of outputting a vector with all fDirIntensities, fLightDirs[2], Norm[z] ; four intensities, one for each light. fDirIntensities, fDirIntensities, vf00[w] ; Clamp the intensity to 0..1 fDirIntensities, fDirIntensities, vf00[x] acc, fLightCols[0], fDirIntensities[x] ; Transform the intensities by the light colour matrix acc, fLightCols[1], fDirIntensities[y] ; This gives the final total directional light colour acc, fLightCols[2], fDirIntensities[z] fDirCols, fAmbient, vf00[w] loi 128 ; Load 128 to I addi.w fDirCols, vf00, i ; ***** AVERAGING OUT LIGHTING FOR VERT ***** add fFinalLight, fDirCols, fPointCols ; ***** CONVERT AND UPLOAD FINAL LIGHT DATA ***** ftoi0 iFinalLight, fFinalLight ; Convert to ints sq iFinalLight, NormStartOut(Counter) ; And write to the output buffer ; ***** LOOP COUNTER/RENDER KICK ***** iaddiu Counter, Counter, 3 ibne Counter, iNumVerts, loop ; Loop until all of the verts in this batch are done. iaddiu iKick, iDBOffset, GifPacketOut lq GP, GifPacket(iDBOffset) ; Copy the GIFTag to the output buffer sq GP, GifPacketOut(iDBOffset) xgkick iKick ; and render! --cont ; --cont is like end, but it really means pause, as this is where the code ; will pick up from when MSCNT is called. b begin ; Which will make it hit this code which takes it back to the start, but ; skips the initialisation which we don't want done twice. --exit --endexit