1212 * ```
1313 */
1414
15- import tgpu , { d } from 'typegpu' ;
15+ import tgpu , { d , std } from 'typegpu' ;
1616// deno-fmt-ignore: just a list of standard functions
17- import { abs , add , cos , max , min , mul , normalize , select , sign , sin , sub , tanh } from 'typegpu/std' ;
17+ import { abs , add , cos , max , min , mul , select , sign , sin , sub , tanh } from 'typegpu/std' ;
1818import { defineControls } from '../../common/defineControls.ts' ;
19+ import {
20+ Camera ,
21+ setupFirstPersonCamera ,
22+ } from '../../common/setup-first-person-camera.ts' ;
1923
2024// NOTE: Some APIs are still unstable (are being finalized based on feedback), but
2125// we can still access them if we know what we're doing.
@@ -52,39 +56,61 @@ const rotateXZ = tgpu.fn([d.f32], d.mat3x3f)((angle) =>
5256 )
5357) ;
5458
59+ export const Ray = d . struct ( {
60+ origin : d . vec4f ,
61+ direction : d . vec4f ,
62+ } ) ;
63+
64+ /**
65+ * Returns a ray direction and ray origin for given uv,
66+ * in accordance to camera.
67+ */
68+ const getRayForUV = ( uv : d . v2f ) => {
69+ 'use gpu' ;
70+ const camera = cameraUniform . $ ;
71+ const farView = camera . projectionInverse . mul ( d . vec4f ( uv , 1 , 1 ) ) ;
72+ const farWorld = camera . viewInverse . mul (
73+ d . vec4f ( farView . xyz . div ( farView . w ) , 1 ) ,
74+ ) ;
75+ const direction = std . normalize ( farWorld . xyz . sub ( camera . pos . xyz ) ) ;
76+ return Ray ( { origin : camera . pos , direction : d . vec4f ( direction , 0 ) } ) ;
77+ } ;
78+
5579// Roots are your GPU handle, and can be used to allocate memory, dispatch
5680// shaders, etc.
5781const root = await tgpu . init ( ) ;
5882
5983// Uniforms are used to send read-only data to the GPU
60- const time = root . createUniform ( d . f32 ) ;
61- const scale = root . createUniform ( d . f32 ) ;
62- const color = root . createUniform ( d . vec3f ) ;
63- const shift = root . createUniform ( d . f32 ) ;
64- const aspectRatio = root . createUniform ( d . f32 ) ;
84+ const autoMoveOffsetUniform = root . createUniform ( d . vec3f ) ;
85+ const controlsOffsetUniform = root . createUniform ( d . f32 ) ;
86+ const colorUniform = root . createUniform ( d . vec3f ) ;
87+ const shiftUniform = root . createUniform ( d . f32 ) ;
6588
6689const fragmentMain = tgpu [ '~unstable' ] . fragmentFn ( {
6790 in : { uv : d . vec2f } ,
6891 out : d . vec4f ,
6992} ) ( ( { uv } ) => {
7093 // Increasing the color intensity
71- const icolor = mul ( color . $ , 4 ) ;
72- const ratio = d . vec2f ( aspectRatio . $ , 1 ) ;
73- const dir = normalize ( d . vec3f ( mul ( uv , ratio ) , - 1 ) ) ;
94+ const icolor = mul ( colorUniform . $ , 4 ) ;
95+
96+ // Calculate ray direction based on UV and camera orientation
97+ const ray = getRayForUV ( uv ) ;
7498
7599 let acc = d . vec3f ( ) ;
76100 let z = d . f32 ( 0 ) ;
77101 for ( let l = 0 ; l < 30 ; l ++ ) {
78- const p = sub ( mul ( z , dir ) , scale . $ ) ;
79- p . x -= time . $ + 3 ;
80- p . z -= time . $ + 3 ;
102+ const p = d . vec3f ( 3 , 0 , 3 )
103+ . add ( controlsOffsetUniform . $ )
104+ . add ( autoMoveOffsetUniform . $ )
105+ . add ( ray . origin . xyz )
106+ . add ( mul ( ray . direction . xyz , z ) ) ;
81107 let q = d . vec3f ( p ) ;
82108 let prox = p . y ;
83109 for ( let i = 40.1 ; i > 0.01 ; i *= 0.2 ) {
84110 q = sub ( i * 0.9 , abs ( sub ( mod ( q , i + i ) , i ) ) ) ;
85111 const minQ = min ( min ( q . x , q . y ) , q . z ) ;
86112 prox = max ( prox , minQ ) ;
87- q = mul ( q , rotateXZ ( shift . $ ) ) ;
113+ q = mul ( q , rotateXZ ( shiftUniform . $ ) ) ;
88114 }
89115 z += prox ;
90116 acc = add ( acc , mul ( sub ( icolor , safeTanh ( p . y + 4 ) ) , 0.1 * prox / ( 1 + z ) ) ) ;
@@ -115,21 +141,33 @@ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
115141const canvas = document . querySelector ( 'canvas' ) as HTMLCanvasElement ;
116142const context = root . configureContext ( { canvas, alphaMode : 'premultiplied' } ) ;
117143
144+ const cameraUniform = root . createUniform ( Camera ) ;
145+ const { cleanupCamera, updatePosition } = setupFirstPersonCamera ( canvas , {
146+ speed : d . vec3f ( 0.001 , 0.1 , 1 ) ,
147+ } , ( props ) => {
148+ cameraUniform . writePartial ( props ) ;
149+ } ) ;
150+
118151const pipeline = root [ '~unstable' ] . createRenderPipeline ( {
119152 vertex : vertexMain ,
120153 fragment : fragmentMain ,
121154 targets : { format : presentationFormat } ,
122155} ) ;
123156
124157let isRunning = true ;
158+ let autoMove = true ;
159+ let autoMoveOffset = d . vec3f ( ) ;
125160
126- function draw ( timestamp : number ) {
161+ function draw ( ) {
127162 if ( ! isRunning ) {
128163 return ;
129164 }
130165
131- aspectRatio . write ( canvas . clientWidth / canvas . clientHeight ) ;
132- time . write ( ( timestamp * 0.001 ) % 1000 ) ;
166+ if ( autoMove && ! document . pointerLockElement ) {
167+ autoMoveOffset = autoMoveOffset . add ( d . vec3f ( 0.01 , 0 , 0.01 ) ) ;
168+ autoMoveOffsetUniform . write ( autoMoveOffset ) ;
169+ }
170+ updatePosition ( ) ;
133171
134172 pipeline
135173 . withColorAttachment ( {
@@ -147,13 +185,19 @@ requestAnimationFrame(draw);
147185// #region Example controls and cleanup
148186
149187export const controls = defineControls ( {
150- scale : {
188+ 'auto move' : {
189+ initial : autoMove ,
190+ onToggleChange ( newValue ) {
191+ autoMove = newValue ;
192+ } ,
193+ } ,
194+ offset : {
151195 initial : 2 ,
152- min : - 15 ,
153- max : 100 ,
196+ min : - 100 ,
197+ max : 15 ,
154198 step : 0.01 ,
155199 onSliderChange ( v ) {
156- scale . write ( v ) ;
200+ controlsOffsetUniform . write ( v ) ;
157201 } ,
158202 } ,
159203 'pattern shift' : {
@@ -162,19 +206,20 @@ export const controls = defineControls({
162206 max : 200 ,
163207 step : 0.001 ,
164208 onSliderChange ( v ) {
165- shift . write ( v / 180 * Math . PI ) ;
209+ shiftUniform . write ( v / 180 * Math . PI ) ;
166210 } ,
167211 } ,
168212 color : {
169213 initial : d . vec3f ( 1 , 0.7 , 0 ) ,
170214 onColorChange ( value ) {
171- color . write ( value ) ;
215+ colorUniform . write ( value ) ;
172216 } ,
173217 } ,
174218} ) ;
175219
176220export function onCleanup ( ) {
177221 isRunning = false ;
222+ cleanupCamera ( ) ;
178223 root . destroy ( ) ;
179224}
180225
0 commit comments