@@ -34,88 +34,104 @@ void main(void) {
3434` ;
3535}
3636
37- // Modulation helpers to smooth the values.
37+ // Helper class to handle uniform updates
3838class UniformValue {
39- constructor ( ) {
40- this . value = 0 ;
41- this . desired = 0 ;
42- this . slow = 10 ;
39+ constructor ( name , count , draw ) {
40+ this . name = name ;
41+ this . draw = draw ;
42+ this . isArray = count > 0 ;
43+ this . value = new Array ( Math . max ( 1 , count ) ) . fill ( 0 ) ;
44+ this . frameModifier = new Array ( Math . max ( 1 , count ) ) . fill ( null ) ;
4345 }
4446
45- get ( elapsed ) {
46- // Adjust the value according to the rate of change
47- const offset = ( this . desired - this . value ) / ( this . slow * Math . max ( 1 , elapsed * 60 ) ) ;
48- // Ignore small changes
49- if ( Math . abs ( offset ) > 1e-3 ) this . value += offset ;
50- return this . value ;
51- }
52- }
53-
54- // Set an uniform value (from a pattern).
55- export function setUniform ( instanceName , name , value , incr , position , slow ) {
56- const instance = _instances [ instanceName ] ;
57- if ( ! instance ) {
58- logger ( '[shader] not loaded yet' , 'warning' ) ;
59- return ;
47+ incr ( value , pos = 0 ) {
48+ const idx = pos % this . value . length ;
49+ this . value [ idx ] += value ;
50+ this . frameModifier [ idx ] = null ;
51+ this . draw ( ) ;
6052 }
6153
62- // console.log('setUniform: ', name, value, position, slow);
63- const uniform = instance . uniforms [ name ] ;
64- if ( uniform ) {
65- let uniformValue ;
66- if ( uniform . count == 0 ) {
67- // This is a single value
68- uniformValue = uniform . value ;
54+ set ( value , pos = 0 ) {
55+ const idx = pos % this . value . length ;
56+ if ( typeof value === 'function' ) {
57+ this . frameModifier [ idx ] = value ;
6958 } else {
70- // This is an array
71- const idx = position % uniform . value . length ;
72- uniformValue = uniform . value [ idx ] ;
59+ this . value [ idx ] = value ;
60+ this . frameModifier [ idx ] = null ;
7361 }
74- uniformValue . slow = slow ;
75- if ( incr ) uniformValue . desired += value ;
76- else uniformValue . desired = value ;
77- } else {
78- logger ( '[shader] unknown uniform: ' + name ) ;
62+ this . draw ( ) ;
63+ }
64+
65+ get ( pos = 0 ) {
66+ return this . value [ pos % this . value . length ] ;
67+ }
68+
69+ _frameUpdate ( elapsed ) {
70+ this . value = this . value . map ( ( value , idx ) =>
71+ this . frameModifier [ idx ] ? this . frameModifier [ idx ] ( value , elapsed ) : value ,
72+ ) ;
73+ return this . isArray ? this . value : this . value [ 0 ] ;
7974 }
8075
81- // Ensure the instance is drawn
82- instance . age = 0 ;
83- if ( ! instance . drawing ) {
84- instance . drawing = requestAnimationFrame ( instance . update ) ;
76+ _resize ( count ) {
77+ if ( count != this . count ) {
78+ this . isArray = count > 0 ;
79+ count = Math . max ( 1 , count ) ;
80+ resizeArray ( this . value , count , 0 ) ;
81+ resizeArray ( this . frameModifier , count , null ) ;
82+ }
8583 }
8684}
8785
88- // Update the uniforms for a given drawFrame call.
89- function updateUniforms ( drawFrame , elapsed , uniforms ) {
90- Object . values ( uniforms ) . forEach ( ( uniform ) => {
91- const value = uniform . count == 0 ? uniform . value . get ( elapsed ) : uniform . value . map ( ( v ) => v . get ( elapsed ) ) ;
86+ // Shrink or extend an array
87+ function resizeArray ( arr , size , defval ) {
88+ if ( arr . length > size ) arr . length = size ;
89+ else arr . push ( ...new Array ( size - arr . length ) . fill ( defval ) ) ;
90+ }
9291
93- // Send the value to the GPU
94- // console.log('updateUniforms:', uniform.name, value);
95- drawFrame . uniform ( uniform . name , value ) ;
96- } ) ;
92+ // Get the size of an uniform
93+ function uniformSize ( funcName ) {
94+ if ( funcName == 'uniform3fv' ) return 3 ;
95+ else if ( funcName == 'uniform4fv' ) return 4 ;
96+ return 1 ;
9797}
9898
9999// Setup the instance's uniform after shader compilation.
100- function setupUniforms ( uniforms , program ) {
101- Object . entries ( program . uniforms ) . forEach ( ( [ name , uniform ] ) => {
100+ function setupUniforms ( instance ) {
101+ const newUniforms = new Set ( ) ;
102+ Object . entries ( instance . program . uniforms ) . forEach ( ( [ name , uniform ] ) => {
102103 if ( name != 'iTime' && name != 'iResolution' ) {
103104 // remove array suffix
104105 const uname = name . replace ( '[0]' , '' ) ;
105- const count = uniform . count | 0 ;
106- if ( ! uniforms [ uname ] || uniforms [ uname ] . count != count ) {
107- // TODO: keep the previous values when the count change:
108- // if the count decreased, then drop the excess, else append new values
109- uniforms [ uname ] = {
110- name,
111- count,
112- value : count == 0 ? new UniformValue ( ) : new Array ( count ) . fill ( ) . map ( ( ) => new UniformValue ( ) ) ,
113- } ;
114- }
106+ newUniforms . add ( uname ) ;
107+ const count = ( uniform . count | 0 ) * uniformSize ( uniform . glFuncName ) ;
108+ if ( ! instance . uniforms [ uname ] )
109+ instance . uniforms [ uname ] = new UniformValue ( name , count , ( ) => {
110+ // Start the drawing loop
111+ instance . age = 0 ;
112+ if ( ! instance . drawing ) {
113+ instance . drawing = requestAnimationFrame ( instance . update ) ;
114+ }
115+ } ) ;
116+ else instance . uniforms [ uname ] . _resize ( count ) ;
115117 }
116118 } ) ;
117- // TODO: remove previous uniform that are no longer used...
118- return uniforms ;
119+
120+ // Remove deleted uniforms
121+ Object . keys ( instance . uniforms ) . forEach ( ( name ) => {
122+ if ( ! newUniforms . has ( name ) ) delete instance . uniforms [ name ] ;
123+ } ) ;
124+ }
125+
126+ // Update the uniforms for a given drawFrame call.
127+ function updateUniforms ( drawFrame , elapsed , uniforms ) {
128+ Object . values ( uniforms ) . forEach ( ( uniform ) => {
129+ const value = uniform . _frameUpdate ( elapsed ) ;
130+
131+ // Send the value to the GPU
132+ // console.log('updateUniforms:', uniform.name, value);
133+ drawFrame . uniform ( uniform . name , value ) ;
134+ } ) ;
119135}
120136
121137// Setup the canvas and return the WebGL context.
@@ -156,8 +172,8 @@ async function initializeShaderInstance(name, code) {
156172 . createPrograms ( [ vertexShader , code ] )
157173 . then ( ( [ program ] ) => {
158174 const drawFrame = app . createDrawCall ( program , arrays ) ;
159- const instance = { app, code, program, arrays, drawFrame, uniforms : setupUniforms ( { } , program ) } ;
160-
175+ const instance = { app, code, program, arrays, drawFrame, uniforms : { } } ;
176+ setupUniforms ( instance ) ;
161177 // Render frame logic
162178 let prev = performance . now ( ) / 1000 ;
163179 instance . age = 0 ;
@@ -189,8 +205,8 @@ async function reloadShaderInstanceCode(instance, code) {
189205 return instance . app . createPrograms ( [ vertexShader , code ] ) . then ( ( [ program ] ) => {
190206 instance . program . delete ( ) ;
191207 instance . program = program ;
192- instance . uniforms = setupUniforms ( instance . uniforms , program ) ;
193208 instance . drawFrame = instance . app . createDrawCall ( program , instance . arrays ) ;
209+ setupUniforms ( instance ) ;
194210 } ) ;
195211}
196212
@@ -207,4 +223,5 @@ export async function loadShader(code = '', name = 'default') {
207223 await reloadShaderInstanceCode ( _instances [ name ] , code ) ;
208224 logger ( '[shader] reloaded' ) ;
209225 }
226+ return _instances [ name ] ;
210227}
0 commit comments