1
+ // PART 1: INITIALIZATION
2
+
3
+ // Step 1: Prepare the environment
4
+
5
+ const canvas = document . querySelector ( 'canvas' ) ;
6
+ const gl = canvas . getContext ( 'webgl2' ) ;
7
+
8
+ // We'll be using transparency when rendering the second
9
+ // program. We'll enable blending before its draw call and
10
+ // disable it after, but we can set the blending function
11
+ // here now since it will never change.
12
+ gl . blendFunc ( gl . SRC_ALPHA , gl . ONE_MINUS_SRC_ALPHA ) ;
13
+
14
+
15
+ // Multiple programs are needed, so let's abstract the
16
+ // creation process
17
+ const createProgram = ( gl , vs , fs ) => {
18
+ const program = gl . createProgram ( ) ;
19
+
20
+ const vertexShader = gl . createShader ( gl . VERTEX_SHADER ) ;
21
+ gl . shaderSource ( vertexShader , vs ) ;
22
+ gl . compileShader ( vertexShader ) ;
23
+ gl . attachShader ( program , vertexShader ) ;
24
+
25
+ const fragmentShader = gl . createShader ( gl . FRAGMENT_SHADER ) ;
26
+ gl . shaderSource ( fragmentShader , fs ) ;
27
+ gl . compileShader ( fragmentShader ) ;
28
+ gl . attachShader ( program , fragmentShader ) ;
29
+
30
+ gl . linkProgram ( program ) ;
31
+
32
+ if ( ! gl . getProgramParameter ( program , gl . LINK_STATUS ) ) {
33
+ console . log ( gl . getShaderInfoLog ( vertexShader ) ) ;
34
+ console . log ( gl . getShaderInfoLog ( fragmentShader ) ) ;
35
+ }
36
+
37
+ return program ;
38
+ } ;
39
+
40
+
41
+
42
+
43
+ // Step 2: Create the triangle program and configure its buffers
44
+ const triangleVerexShaderSource =
45
+ `#version 300 es
46
+ #pragma vscode_glsllint_stage: vert
47
+
48
+ uniform vec2 offset;
49
+
50
+ layout(location=0) in vec4 aPosition;
51
+ layout(location=1) in vec4 aColor;
52
+
53
+ out vec4 vColor;
54
+
55
+ void main()
56
+ {
57
+ vColor = aColor;
58
+ gl_Position = aPosition + vec4(offset, 0.0, 0.0);
59
+ }` ;
60
+
61
+ const triangleFragmentShaderSource =
62
+ `#version 300 es
63
+ #pragma vscode_glsllint_stage: frag
64
+
65
+ precision mediump float;
66
+
67
+ in vec4 vColor;
68
+
69
+ layout(location=0) out vec4 fragColor;
70
+ layout(location=1) out vec4 solidColor;
71
+
72
+ void main()
73
+ {
74
+ fragColor = vColor;
75
+ solidColor = vec4(0.0, 1.0, 0.0, 1.0);
76
+ }` ;
77
+
78
+ const triangleProgram = createProgram ( gl , triangleVerexShaderSource , triangleFragmentShaderSource ) ;
79
+
80
+ const triangleData = new Float32Array ( [
81
+ // Pos (xyz) // Color (rgb)
82
+ - .5 , - .5 , - .5 , 0 , 0 , 1 ,
83
+ 0.5 , - .5 , - .5 , 0 , 0 , 1 ,
84
+ 0.0 , 0.4 , - .5 , 0 , 0 , 1 ,
85
+
86
+ - .5 , 0.5 , 0.5 , 1 , 0 , 0 ,
87
+ 0.5 , 0.5 , 0.5 , 1 , 0 , 0 ,
88
+ 0.0 , - .4 , 0.5 , 1 , 0 , 0 ,
89
+
90
+ ] ) ;
91
+
92
+ // Two programs? Use vertex array objects. They will
93
+ // allow you to easily and instantly bind the buffers
94
+ // you need before you issue your draw calls.
95
+ const triangleVAO = gl . createVertexArray ( ) ;
96
+ gl . bindVertexArray ( triangleVAO ) ;
97
+
98
+ const triangleBuffer = gl . createBuffer ( ) ;
99
+ gl . bindBuffer ( gl . ARRAY_BUFFER , triangleBuffer ) ;
100
+ gl . bufferData ( gl . ARRAY_BUFFER , triangleData , gl . STATIC_DRAW ) ;
101
+ gl . vertexAttribPointer ( 0 , 3 , gl . FLOAT , false , 24 , 0 ) ;
102
+ gl . vertexAttribPointer ( 1 , 3 , gl . FLOAT , false , 24 , 12 ) ;
103
+ gl . enableVertexAttribArray ( 0 ) ;
104
+ gl . enableVertexAttribArray ( 1 ) ;
105
+
106
+ gl . bindVertexArray ( null ) ;
107
+
108
+ // And get the uniform location in the first program of its `offset` uniform
109
+ const offsetUniformLocation = gl . getUniformLocation ( triangleProgram , 'offset' ) ;
110
+
111
+ // Step 3: Create the (emtpy) texture
112
+ const fragColorTexture = gl . createTexture ( ) ;
113
+ gl . bindTexture ( gl . TEXTURE_2D , fragColorTexture ) ;
114
+ gl . texStorage2D ( gl . TEXTURE_2D , 1 , gl . RGBA8 , 480 , 480 ) ;
115
+
116
+ const solidColorTexture = gl . createTexture ( ) ;
117
+ gl . bindTexture ( gl . TEXTURE_2D , solidColorTexture ) ;
118
+ gl . texStorage2D ( gl . TEXTURE_2D , 1 , gl . RGBA8 , 480 , 480 ) ;
119
+
120
+ gl . bindTexture ( gl . TEXTURE_2D , null ) ;
121
+
122
+ const renderbuffer = gl . createRenderbuffer ( ) ;
123
+ gl . bindRenderbuffer ( gl . RENDERBUFFER , renderbuffer ) ;
124
+ gl . renderbufferStorage ( gl . RENDERBUFFER , gl . DEPTH_COMPONENT16 , 480 , 480 ) ;
125
+ gl . bindRenderbuffer ( gl . RENDERBUFFER , null ) ;
126
+
127
+
128
+ // Step 5: Add the quad program and configure its buffers
129
+ const quadVertexShaderSource =
130
+ `#version 300 es
131
+ #pragma vscode_glsllint_stage: vert
132
+
133
+ layout(location=0) in vec4 aPosition;
134
+ layout(location=1) in vec2 aTexCoord;
135
+
136
+ out vec2 vTexCoord;
137
+
138
+ void main()
139
+ {
140
+ gl_Position = aPosition;
141
+ vTexCoord = aTexCoord;
142
+ }` ;
143
+
144
+
145
+ const quadFragmentShaderSource =
146
+ `#version 300 es
147
+ #pragma vscode_glsllint_stage: frag
148
+
149
+ precision mediump float;
150
+
151
+ uniform sampler2D sampler;
152
+
153
+ in vec2 vTexCoord;
154
+
155
+ out vec4 fragColor;
156
+
157
+ void main()
158
+ {
159
+ fragColor = texture(sampler, vTexCoord);
160
+ }` ;
161
+
162
+ const quadProgram = createProgram ( gl , quadVertexShaderSource , quadFragmentShaderSource ) ;
163
+
164
+ const quadData = new Float32Array ( [
165
+ // Pos (xy) // UV coordinate
166
+ - 1 , 1 , 0 , 1 ,
167
+ - 1 , - 1 , 0 , 0 ,
168
+ 1 , 1 , 1 , 1 ,
169
+ 1 , - 1 , 1 , 0 ,
170
+ ] ) ;
171
+
172
+ const quadVAO = gl . createVertexArray ( ) ;
173
+ gl . bindVertexArray ( quadVAO ) ;
174
+
175
+ const quadBuffer = gl . createBuffer ( ) ;
176
+ gl . bindBuffer ( gl . ARRAY_BUFFER , quadBuffer ) ;
177
+ gl . bufferData ( gl . ARRAY_BUFFER , quadData , gl . STATIC_DRAW ) ;
178
+ gl . vertexAttribPointer ( 0 , 2 , gl . FLOAT , false , 16 , 0 ) ;
179
+ gl . vertexAttribPointer ( 1 , 2 , gl . FLOAT , false , 16 , 8 ) ;
180
+ gl . enableVertexAttribArray ( 0 ) ;
181
+ gl . enableVertexAttribArray ( 1 ) ;
182
+
183
+ gl . bindVertexArray ( null ) ;
184
+
185
+
186
+ // Step 5: Initialize the framebuffer
187
+ // a) attach the two textures color attachments
188
+ // b) attach the depth buffer renderbuffer
189
+ // c) tell WebGL which outputs to draw
190
+ const fbo = gl . createFramebuffer ( ) ;
191
+ gl . bindFramebuffer ( gl . FRAMEBUFFER , fbo ) ;
192
+ gl . framebufferTexture2D ( gl . FRAMEBUFFER , gl . COLOR_ATTACHMENT0 , gl . TEXTURE_2D , fragColorTexture , 0 ) ;
193
+ gl . framebufferTexture2D ( gl . FRAMEBUFFER , gl . COLOR_ATTACHMENT1 , gl . TEXTURE_2D , solidColorTexture , 0 ) ;
194
+ gl . framebufferRenderbuffer ( gl . FRAMEBUFFER , gl . DEPTH_ATTACHMENT , gl . RENDERBUFFER , renderbuffer ) ;
195
+
196
+ gl . drawBuffers ( [ gl . COLOR_ATTACHMENT0 , gl . COLOR_ATTACHMENT1 ] ) ;
197
+
198
+ gl . bindFramebuffer ( gl . FRAMEBUFFER , null ) ;
199
+
200
+
201
+ //PART 2: ANIMATE & RENDER
202
+ let angle = 0 ;
203
+ const animate = ( ) => {
204
+ angle += 0.05 ;
205
+
206
+ // Step 1: Draw the first triangle to the FBO with depth testing enabled
207
+ gl . useProgram ( triangleProgram ) ;
208
+ gl . uniform2f ( offsetUniformLocation , Math . cos ( angle ) , Math . sin ( angle ) ) ;
209
+ gl . bindVertexArray ( triangleVAO ) ;
210
+ gl . enable ( gl . DEPTH_TEST ) ;
211
+ gl . bindFramebuffer ( gl . FRAMEBUFFER , fbo ) ;
212
+
213
+ gl . clear ( gl . COLOR_BUFFER_BIT | gl . DEPTH_BUFFER_BIT ) ;
214
+
215
+ gl . drawArrays ( gl . TRIANGLES , 0 , 6 ) ;
216
+ gl . bindFramebuffer ( gl . FRAMEBUFFER , null ) ;
217
+ gl . disable ( gl . DEPTH_TEST ) ;
218
+
219
+ // Step 2: Draw the quad and pick a texture to render
220
+ gl . useProgram ( quadProgram ) ;
221
+ gl . bindVertexArray ( quadVAO ) ;
222
+ gl . bindTexture ( gl . TEXTURE_2D , fragColorTexture ) ; // fragColorTexture or solidColorTexture
223
+ gl . enable ( gl . BLEND ) ;
224
+ gl . drawArrays ( gl . TRIANGLE_STRIP , 0 , 4 ) ;
225
+ gl . disable ( gl . BLEND ) ;
226
+ gl . bindTexture ( gl . TEXTURE_2D , null ) ;
227
+ gl . bindVertexArray ( null ) ;
228
+
229
+ requestAnimationFrame ( animate ) ;
230
+ } ;
231
+ animate ( ) ;
0 commit comments