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.