PageSourceSearch

https://binomial.biz/ktx2_studio/renderer.js

js binomial.biz collected 2026-09-25 22:09:03 UTC 18,421 bytes, 554 lines download raw bytes

1/**
2 * Constructs a renderer object.
3 * @param {WebGLRenderingContext} gl The GL context.
4 * @constructor
5 */
6var Renderer = function (gl) {
7   /**
8    * The GL context.
9    * @type {WebGLRenderingContext}
10    * @private
11    */
12   this.gl_ = gl;
13
14   /**
15    * A vertex buffer containing a single quad with xy coordinates from [-1,-1]
16    * to [1,1] and uv coordinates from [0,0] to [1,1].
17    * @private
18    */
19   this.quadVertexBuffer_ = gl.createBuffer();
20   gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer_);
21
22   var vertices = new Float32Array(
23      [-1.0, -1.0, 0.0, 1.0,
24      +1.0, -1.0, 1.0, 1.0,
25      -1.0, +1.0, 0.0, 0.0,
26         1.0, +1.0, 1.0, 0.0]);
27		 
28//   var vertices = new Float32Array(
29//      [-1.0, -1.0, 0.0, .5,
30//      +1.0, -1.0, .5, .5,
31//      -1.0, +1.0, 0.0, 0.0,
32//         1.0, +1.0, .5, 0.0]);
33		 
34   gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
35
36   /**
37    * The default display program (no deblocking).
38    * @private
39    */
40   this.program_ = this.buildProgram_(
41      Renderer.vertexShaderSource_,
42      Renderer.fragmentShaderSource_);
43
44   /**
45    * The deblocking program.
46    * @private
47    */
48   this.deblockProgram_ = this.buildProgram_(
49      Renderer.vertexShaderSource_,
50      Renderer.deblockFragmentShaderSource_);
51
52   // Bind the default program so any code relying on state after construction
53   // sees the same program active as before.
54   gl.useProgram(this.program_.program);
55   gl.enableVertexAttribArray(0);
56
57   gl.enable(gl.DEPTH_TEST);
58   gl.disable(gl.CULL_FACE);
59};
60
61
62/**
63 * Compiles, links, and introspects a program. Returns an object bundling the
64 * program handle with its cached uniform and attribute locations.
65 * @param {string} vertexSource
66 * @param {string} fragmentSource
67 * @return {{program: WebGLProgram, uniforms: !Object, attribs: !Object}}
68 * @private
69 */
70Renderer.prototype.buildProgram_ = function (vertexSource, fragmentSource) {
71   var gl = this.gl_;
72   var program = gl.createProgram();
73   var vs = this.compileShader_(vertexSource, gl.VERTEX_SHADER);
74   var fs = this.compileShader_(fragmentSource, gl.FRAGMENT_SHADER);
75
76   gl.attachShader(program, vs);
77   gl.attachShader(program, fs);
78   gl.bindAttribLocation(program, 0, 'vert');
79   gl.linkProgram(program);
80
81   if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
82      var log = gl.getProgramInfoLog(program);
83      console.error('Program link failed:\n' + log);
84      throw new Error('Program link failed');
85   }
86
87   var uniforms = {};
88   var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
89   for (var i = 0; i < /** @type {number} */(uniformCount); i++) {
90      var uinfo = gl.getActiveUniform(program, i);
91      uniforms[uinfo.name] = gl.getUniformLocation(program, uinfo.name);
92   }
93
94   var attribs = {};
95   var attribCount = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
96   for (var j = 0; j < /** @type {number} */(attribCount); j++) {
97      var ainfo = gl.getActiveAttrib(program, j);
98      attribs[ainfo.name] = gl.getAttribLocation(program, ainfo.name);
99   }
100
101   return { program: program, uniforms: uniforms, attribs: attribs };
102};
103
104
105Renderer.prototype.finishInit = function () {
106   //this.draw();
107};
108
109
110/**
111 * Sets the color space the compositor uses to interpret backbuffer pixels.
112 * Pure display-side knob: does not modify pixel values, just relabels them.
113 * No-op (returns false) on browsers/contexts that don't support the property
114 * or don't accept the requested value.
115 * @param {string} colorSpace e.g. "srgb" or "display-p3"
116 * @return {boolean} true if the requested color space is now active.
117 */
118Renderer.prototype.setCanvasColorSpace = function (colorSpace) {
119   var gl = this.gl_;
120   if (typeof gl.drawingBufferColorSpace === 'undefined') return false;
121   try {
122      gl.drawingBufferColorSpace = colorSpace;
123   } catch (e) {
124      return false;
125   }
126   return gl.drawingBufferColorSpace === colorSpace;
127};
128Renderer.prototype.createDxtTexture = function (dxtData, width, height, format) {
129   var gl = this.gl_;
130   var tex = gl.createTexture();
131   gl.bindTexture(gl.TEXTURE_2D, tex);
132   gl.compressedTexImage2D(
133      gl.TEXTURE_2D,
134      0,
135      format,
136      width,
137      height,
138      0,
139      dxtData);
140   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
141   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
142   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
143   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
144   gl.bindTexture(gl.TEXTURE_2D, null);
145   return tex;
146};
147
148Renderer.prototype.createCompressedTexture = function (data, width, height, format) {
149   var gl = this.gl_;
150   var tex = gl.createTexture();
151   gl.bindTexture(gl.TEXTURE_2D, tex);
152   gl.compressedTexImage2D(
153      gl.TEXTURE_2D,
154      0,
155      format,
156      width,
157      height,
158      0,
159      data);
160   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
161   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
162   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
163   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
164   gl.bindTexture(gl.TEXTURE_2D, null);
165   return tex;
166};
167
168Renderer.prototype.createHalfRGBATexture = function (data, width, height, format) {
169   var gl = this.gl_;
170   var tex = gl.createTexture();
171   gl.bindTexture(gl.TEXTURE_2D, tex);
172   gl.texImage2D(
173      gl.TEXTURE_2D,
174      0,
175      gl.RGBA,
176      width,
177      height,
178      0,
179      gl.RGBA,
180      format,
181      data);
182   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
183   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
184   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
185   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
186   gl.bindTexture(gl.TEXTURE_2D, null);
187   return tex;
188};
189
190// WebGL requires each row of rgb565Data to be aligned on a 4-byte boundary.            
191Renderer.prototype.createRgb565Texture = function (rgb565Data, width, height) {
192   var gl = this.gl_;
193   var tex = gl.createTexture();
194   gl.bindTexture(gl.TEXTURE_2D, tex);
195   
196   gl.texImage2D(
197      gl.TEXTURE_2D,
198      0,
199      gl.RGB,
200      width,
201      height,
202      0,
203      gl.RGB,
204      gl.UNSIGNED_SHORT_5_6_5,
205      rgb565Data);
206   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
207   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
208   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
209   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
210   gl.bindTexture(gl.TEXTURE_2D, null);
211   return tex;
212};
213
214Renderer.prototype.createRgbaTexture = function (rgbaData, width, height) {
215   var gl = this.gl_;
216   var tex = gl.createTexture();
217   gl.bindTexture(gl.TEXTURE_2D, tex);
218   gl.texImage2D(
219      gl.TEXTURE_2D,
220      0,
221      gl.RGBA,
222      width,
223      height,
224      0,
225      gl.RGBA,
226      gl.UNSIGNED_BYTE,
227      rgbaData);
228   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
229   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
230   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
231   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
232   gl.bindTexture(gl.TEXTURE_2D, null);
233   return tex;
234};
235
236
237/**
238 * Draws the given texture across the viewport.
239 *
240 * Signature is preserved exactly from the original, pre-deblocking Renderer.
241 * Existing call sites do not need to change. For the deblocking variant, use
242 * drawTextureDeblocked.
243 *
244 * @param {WebGLTexture} texture
245 * @param {number} width
246 * @param {number} height
247 * @param {number} mode 0=normal, 1=RGB only, 2=alpha only, 3/4/5=R/G/B isolate
248 * @param {number} scale Exposure multiplier applied to RGB.
249 * @param {boolean} linearToSRGBFlag
250 * @param {boolean} useLinearFiltering
251 */
252Renderer.prototype.drawTexture = function (texture, width, height, mode, scale, linearToSRGBFlag, useLinearFiltering) {
253   var gl = this.gl_;
254   var prog = this.program_;
255   gl.useProgram(prog.program);
256
257   // draw scene
258   gl.clearColor(0, 0, 0, 1);
259   gl.clearDepth(1.0);
260   gl.viewport(0, 0, width, height);
261   gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
262
263   gl.activeTexture(gl.TEXTURE0);
264   gl.bindTexture(gl.TEXTURE_2D, texture);
265
266   // Point vs. bilinear sampling (no mipmaps involved here)
267   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, useLinearFiltering ? gl.LINEAR : gl.NEAREST);
268   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, useLinearFiltering ? gl.LINEAR : gl.NEAREST);
269
270   gl.uniform1i(prog.uniforms.texSampler, 0);
271
272   var x = 0.0;
273   var y = 0.0;
274   if (mode == 1)
275      x = 1.0;
276   else if (mode == 2)
277      y = 1.0;
278
279   gl.uniform4f(prog.uniforms.control, x, y, scale, linearToSRGBFlag ? 1.0 : 0.0);
280
281   var a = 1.0 / width;
282   var b = 1.0 / height;
283   var c = (mode >
283= 3) ? (mode - 2) : 0;  // mode 3->1(R), 4->2(G), 5->3(B)
284   var d = 0;
285   gl.uniform4f(prog.uniforms.control2, a, b, c, d);
286
287   gl.enableVertexAttribArray(prog.attribs.vert);
288   gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer_);
289   gl.vertexAttribPointer(prog.attribs.vert, 4, gl.FLOAT,
290      false, 0, 0);
291   gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
292};
293
294
295/**
296 * Draws the given texture across the viewport with a block-edge deblocking
297 * filter applied. Intended for previewing block-compressed textures (DXT, BC,
298 * ASTC, etc.) where compression blocks can introduce visible seams.
299 *
300 * The post-processing knobs (mode, scale, linearToSRGBFlag, useLinearFiltering)
301 * behave exactly as in drawTexture, and run on the deblocked RGBA.
302 *
303 * width/height are the texture's native dimensions (they double as viewport
304 * size because this renderer always draws 1:1).
305 *
306 * @param {WebGLTexture} texture
307 * @param {number} width
308 * @param {number} height
309 * @param {number} mode
310 * @param {number} scale
311 * @param {boolean} linearToSRGBFlag
312 * @param {boolean} useLinearFiltering
313 * @param {number} blockWidth Block width in texels (e.g. 4 for DXT/BC).
314 * @param {number} blockHeight Block height in texels.
315 * @param {boolean} showEdgeWeights Visualize the edge-weight mask instead of
316 *     the deblocked image.
317 */
318Renderer.prototype.drawTextureDeblocked = function (texture, width, height, mode, scale, linearToSRGBFlag, useLinearFiltering, blockWidth, blockHeight, showEdgeWeights) {
319   var gl = this.gl_;
320   var prog = this.deblockProgram_;
321   gl.useProgram(prog.program);
322
323   gl.clearColor(0, 0, 0, 1);
324   gl.clearDepth(1.0);
325   gl.viewport(0, 0, width, height);
326   gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
327
328   gl.activeTexture(gl.TEXTURE0);
329   gl.bindTexture(gl.TEXTURE_2D, texture);
330
331   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, useLinearFiltering ? gl.LINEAR : gl.NEAREST);
332   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, useLinearFiltering ? gl.LINEAR : gl.NEAREST);
333
334   gl.uniform1i(prog.uniforms.texSampler, 0);
335
336   var x = 0.0;
337   var y = 0.0;
338   if (mode == 1)
339      x = 1.0;
340   else if (mode == 2)
341      y = 1.0;
342
343   gl.uniform4f(prog.uniforms.control, x, y, scale, linearToSRGBFlag ? 1.0 : 0.0);
344
345   var a = 1.0 / width;
346   var b = 1.0 / height;
347   var c = (mode >= 3) ? (mode - 2) : 0;
348   var d = 0;
349   gl.uniform4f(prog.uniforms.control2, a, b, c, d);
350
351   // texSize: xy = texture dims, zw = block dims.
352   gl.uniform4f(prog.uniforms.texSize, width, height, blockWidth, blockHeight);
353
354   // const0.x: master deblock enable. const0.y: edge-weight visualization.
355   gl.uniform4f(prog.uniforms.const0, 1.0, showEdgeWeights ? 1.0 : 0.0, 0.0, 0.0);
356
357   gl.enableVertexAttribArray(prog.attribs.vert);
358   gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer_);
359   gl.vertexAttribPointer(prog.attribs.vert, 4, gl.FLOAT,
360      false, 0, 0);
361   gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
362};
363
364
365/**
366 * Compiles a GLSL shader and returns a WebGLShader.
367 * @param {string} shaderSource The shader source code string.
368 * @param {number} type Either VERTEX_SHADER or FRAGMENT_SHADER.
369 * @return {WebGLShader} The new WebGLShader.
370 * @private
371 */
372Renderer.prototype.compileShader_ = function (shaderSource, type) {
373   var gl = this.gl_;
374   var shader = gl.createShader(type);
375   gl.shaderSource(shader, shaderSource);
376   gl.compileShader(shader);
377   
378     // Check for errors
379   const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
380   if (!compiled) {
381        const errorLog = gl.getShaderInfoLog(shader);
382        console.error(`Error compiling ${type === gl.VERTEX_SHADER ? 'vertex' : 'fragment'} shader:\n${errorLog}`);
383        gl.deleteShader(shader); // Cleanup shader object
384        throw new Error('Shader compilation failed');
385   }
386    
387   return shader;
388};
389
390
391/**
392 * @type {string}
393 * @private
394 */
395Renderer.vertexShaderSource_ = /* glsl */ `
396   attribute vec4 vert;
397   varying vec2 v_texCoord;
398
399   void main() {
400      gl_Position = vec4(vert.xy, 0.0, 1.0);
401      v_texCoord = vert.zw;
402   }
403`;
404
405
406/**
407 * @type {string}
408 * @private
409 */
410Renderer.fragmentShaderSource_ = /* glsl */ `
411   precision highp float;
412
413   uniform sampler2D texSampler;
414   uniform vec4 control;
415   uniform vec4 control2;
416   varying vec2 v_texCoord;
417
418   vec3 linearToSrgb(vec3 linearRGB) {
419      vec3 srgbLow  = linearRGB * 12.92;
420      vec3 srgbHigh = 1.055 * pow(linearRGB, vec3(1.0 / 2.4)) - 0.055;
421      return clamp(mix(srgbLow, srgbHigh, step(0.0031308, linearRGB)), 0.0, 1.0);
422   }
423
424   void main() {
425      vec4 c = texture2D(texSampler, v_texCoord);
426      c.rgb *= control.z;
427
428      if (control.x > 0.0) {
429         c.w = 1.0;
430      } else if (control.y > 0.0) {
431         c.rgb = c.aaa;
432         c.w = 1.0;
433      }
434
435      if      (control2.z == 1.0) { c.rgb = vec3(c.r); c.w = 1.0; }
436      else if (control2.z == 2.0) { c.rgb = vec3(c.g); c.w = 1.0; }
437      else if (control2.z == 3.0) { c.rgb = vec3(c.b); c.w = 1.0; }
438
439      if (control.w > 0.0)
440         c.rgb = linearToSrgb(c.rgb);
441
442      gl_FragColor = c;
443   }
444`;
445
446
447/**
448 * Deblocking fragment shader. Structurally identical to fragmentShaderSource_
449 * except the single texture2D(...) fetch at the top is replaced by a block-
450 * aware 3-tap separable filter that softens texels near block boundaries. The
451 * existing post-process chain (exposure, alpha-only, channel isolate,
452 * linear->sRGB) then runs on the deblocked RGBA unchanged.
453 *
454 * Uniforms:
455 *   texSize  xy = source texture dimensions in texels,
456 *            zw = block dimensions in texels (e.g. 4x4 for DXT/BC7, ASTC sizes
457 *                 vary)
458 *   const0   x  = master deblock enable (>0.5),
459 *            y  = edge-weight visualization (>0.5)
460 *
461 * This shader assumes the texture is drawn 2D with no mipmap chain
462 * (mipScale = 1). If you later add mipmapping, restore the dFdx/dFdy-based
463 * mipScale derivation and add the OES_standard_derivatives extension.
464 * @type {string}
465 * @private
466 */
467Renderer.deblockFragmentShaderSource_ = /* glsl */ `
468   precision highp float;
469
470   uniform sampler2D texSampler;
471   uniform vec4 control;
472   uniform vec4 control2;
473   uniform vec4 texSize;  // xy = tex dims, zw = block dims
474   uniform vec4 const0;   // x = deblock on, y = show edge weights
475   varying vec2 v_texCoord;
476
477   vec3 linearToSrgb(vec3 linearRGB) {
478      vec3 srgbLow  = linearRGB * 12.92;
479      vec3 srgbHigh = 1.055 * pow(linearRGB, vec3(1.0 / 2.4)) - 0.055;
480      return clamp(mix(srgbLow, srgbHigh, step(0.0031308, linearRGB)), 0.0, 1.0);
481   }
482
483   void main() {
484      vec2 texDim    = texSize.xy;
485      vec2 blockSize = texSize.zw;
486
487      vec2 texelStep = 1.0 / texDim;             // one texel in mip0 space
488      vec2 texelPos  = (v_texCoord * texDim);    // texel coord in mip0 space
489      vec2 blockPos  = mod(texelPos, blockSize); // texel position within the current block
490
491      vec4 c = texture2D(texSampler, v_texCoord);
492	  
493	  vec4 l1 = texture2D(texSampler, v_texCoord - vec2(texelStep.x, 0.0));
494      vec4 r1 = texture2D(texSampler, v_texCoord + vec2(texelStep.x, 0.0));
495      vec4 u1 = texture2D(texSampler, v_texCoord - vec2(0.0, texelStep.y));
496      vec4 d1 = texture2D(texSampler, v_texCoord + vec2(0.0, texelStep.y));
497      	  
498      if (const0.x > 0.5) {
499	     // Note this shader assumes a quad will be rendered to a viewport the same size as the texture, with point/nearest sampling. If you're going to render with bilinear filtering or mipmapping, this shader isn't the right one.
500         const float falloff = 1.0;
501
502         float leftProx   = 1.0 - clamp(blockPos.x / falloff, 0.0, 1.0);
503         float rightProx  = 1.0 - clamp((blockSize.x - blockPos.x) / falloff, 0.0, 1.0);
504         float topProx    = 1.0 - clamp(blockPos.y / falloff, 0.0, 1.0);
505         float bottomProx = 1.0 - clamp((blockSize.y - blockPos.y) / falloff, 0.0, 1.0);
506
507         // Scale weights by 2.0 to ensure the inner ring of samples have 1.0 edge weights (if we knew bilinear sampling was going to be used, this would be 1.0).
508		 const float K = 2.0;
509		 float horizWeight = min(1.0, K*max(leftProx, rightProx));
510         float vertWeight  = min(1.0, K*max(topProx, bottomProx));
511         float edgeWeight  = max(horizWeight, vertWeight);
512
513         if (edgeWeight > 0.0) {
514            vec4 c0 = c;
515                        
516            vec4 filteredH = (l1 + c0 + r1) * (1.0 / 3.0);
517            vec4 filteredV = (u1 + c0 + d1) * (1.0 / 3.0);
518
519            float strengthH = horizWeight;
520            float strengthV = vertWeight;
521            vec4 horizColor = mix(c0, filteredH, strengthH);
522            vec4 vertColor  = mix(c0, filteredV, strengthV);
523
524            float totalW = strengthH + strengthV;
525            if (totalW > 0.0)
526               c = (horizColor * strengthH + vertColor * strengthV) / totalW;
527         }
528
529         // Block edge visualization.
530         if (const0.y > 0.5) {
531            c = vec4(edgeWeight, edgeWeight, edgeWeight, 1.0);
532         }
533      }
534
535      // --- Post-process chain (identical to fragmentShaderSource_) ---
536      c.rgb *= control.z;
537
538      if (control.x > 0.0) {
539         c.w = 1.0;
540      } else if (control.y > 0.0) {
541         c.rgb = c.aaa;
542         c.w = 1.0;
543      }
544
545      if      (control2.z == 1.0) { c.rgb = vec3(c.r); c.w = 1.0; }
546      else if (control2.z == 2.0) { c.rgb = vec3(c.g); c.w = 1.0; }
547      else if (control2.z == 3.0) { c.rgb = vec3(c.b); c.w = 1.0; }
548
549      if (control.w > 0.0)
550         c.rgb = linearToSrgb(c.rgb);
551
552      gl_FragColor = c;
553   }
554`;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.