我正在为我的项目构建多通道渲染器。问题是,我甚至无法显示单张照片。我已经看过WebGL Inspector了,但一切看起来都很好(我查了一个更简单的版本,我知道它正在工作,而且一切看起来都差不多了。)
[edit] 这是minimal codepen。我只提取了WebGL调用,因为其余的代码超出了范围。我把它留在下面作为参考。
以下是codepen,debug codepen和参考源代码(尽管它的大小,它实际上并没有做很多工作):
class Shader {
constructor( { fragment, vertex, INPUT_SOURCES = 1 } ) {
this.INPUT_SOURCES = INPUT_SOURCES;
this.fragment = fragment;
this.vertex = vertex;
}
getResolution( previous ) {
return previous;
}
buildProgram( context ) {
let fragmentPrefix = `
#define uInputSample uInputSample_Prev0
#define uInputResolution uInputResolution_Prev0
`;
let vertexPrefix = `
#define uInputSample uInputSample_Prev0
#define uInputResolution uInputResolution_Prev0
`;
let fragment = context.createShader( context.gl.FRAGMENT_SHADER, fragmentPrefix + this.fragment );
let vertex = context.createShader( context.gl.VERTEX_SHADER, vertexPrefix + this.vertex );
return context.createProgram( fragment, vertex );
}
}
class RenderOutput {
constructor( ) {
this.resolution = { width : 0, height : 0 };
this.framebuffer = null;
this.texture = null;
}
}
class RenderPass {
constructor( { } ) {
this.previous = null;
this.next = null;
}
static link( from, to ) {
if ( to.previous )
to.remove( );
if ( from.next )
from.next.remove( );
from.next = to;
to.previous = from;
from.refreshOutput( );
}
static unlink( element ) {
if ( ! element.previous )
return ;
let previous = element.previous;
element.previous = null;
previous.next = null;
element.refreshInputs( );
previous.refreshOutput( );
}
append( element ) {
RenderPass.link( this, element );
}
remove( ) {
RenderPass.unlink( this );
}
}
class RenderPassShader extends RenderPass {
constructor( { context, shader }, previous = null ) {
super( );
this.context = context;
this.shader = shader;
this.program = shader.buildProgram( this.context );
this.uInputSampleLocations = [ ];
this.uInputResolutionLocations = [ ];
this.inputs = null;
this.output = new RenderOutput( );
this.resolution = null;
// -- switch on uniforms
this.context.gl.useProgram( this.program );
// -- locate uniforms
this.uOutputResolutionLocation = this.context.gl.getUniformLocation( this.program, 'uOutputResolution' );
this.uScreenResolutionLocation = this.context.gl.getUniformLocation( this.program, 'uScreenResolution' );
for ( let t = 0, T = this.shader.INPUT_SOURCES; t < T; ++ t ) {
this.uInputSampleLocations.push( this.context.gl.getUniformLocation( this.program, `uInputSample_Prev${t}` ) );
this.uInputResolutionLocations.push( this.context.gl.getUniformLocation( this.program, `uInputResolution_Prev${t}` ) );
}
// -- locate attributes
this.aVertexPositionLocation = this.context.gl.getAttribLocation( this.program, 'aVertexPosition' );
this.aVertexTextureUvLocation = this.context.gl.getAttribLocation( this.program, 'aVertexTextureUv' );
this.context.gl.bindBuffer( this.context.vertexPositionBuffer.bufferTarget, this.context.vertexPositionBuffer );
this.context.gl.vertexAttribPointer( this.aVertexPositionLocation, this.context.vertexPositionBuffer.itemSize, this.context.gl.FLOAT, false, 0, 0 );
this.context.gl.bindBuffer( this.context.vertexPositionBuffer.bufferTarget, null );
this.context.gl.bindBuffer( this.context.vertexTextureUvBuffer.bufferTarget, this.context.vertexTextureUvBuffer );
this.context.gl.vertexAttribPointer( this.aVertexTextureUvLocation, this.context.vertexTextureUvBuffer.itemSize, this.context.gl.FLOAT, false, 0, 0 );
this.context.gl.bindBuffer( this.context.vertexTextureUvBuffer.bufferTarget, null );
// -- switch off shader
this.context.gl.useProgram( null );
// -- initialize
this.refreshInputs( );
// -- attach
previous && previous.append( this );
}
refreshOutput( ) {
if ( this.next && ! this.output.texture ) {
let { width, height } = this.output.resolution;
this.output.framebuffer = this.context.createFramebuffer( );
this.output.texture = this.context.createTexture( );
this.context.setTextureSize( this.output.texture, width, height );
this.context.gl.bindFramebuffer( this.context.gl.FRAMEBUFFER, this.output.framebuffer );
this.context.gl.framebufferTexture2D( this.context.gl.FRAMEBUFFER, this.context.gl.COLOR_ATTACHMENT0, this.context.gl.TEXTURE_2D, this.output.texture, 0 );
this.context.gl.bindFramebuffer( this.context.gl.FRAMEBUFFER, null );
this.next.refreshInputs( );
} else if ( ! this.next && this.output.texture ) {
this.context.deleteFramebuffer( this.output.framebuffer );
this.context.deleteTexture( this.output.texture );
this.output.framebuffer = null;
this.output.texture = null;
}
}
refreshInputs( ) {
this.context.useProgram( this.program, ( ) => {
this.inputs = this.previous ? this.previous.getOutputs( ) : [ ];
this.valid = this.shader.INPUT_SOURCES <= this.inputs.length;
let { width, height } = this.getResolution( );
this.output.resolution = { width, height };
if ( this.output.texture )
this.context.setTextureSize( this.output.texture, width, height );
for ( let t = 0, T = Math.min( this.inputs.length, this.shader.INPUT_SOURCES ); t < T; ++ t ) {
this.context.gl.uniform1i( this.uInputSampleLocations[ t ], this.inputs[ t ].texture );
this.context.gl.uniform2f( this.uInputResolutionLocations[ t ], this.inputs[ t ].resolution.width, this.inputs[ t ].resolution.height );
}
} );
if ( this.next ) {
this.next.refreshInputs( );
}
}
getResolution( ) {
return this.shader.getResolution( this );
}
getOutputs( ) {
let previous = this.previous ? this.previous.getOutputs( ) : [ ];
return [ this.output ].concat( previous );
}
render( ) {
if ( ! this.valid )
throw new Error( 'Invalid pass' );
this.context.gl.useProgram( this.program );
this.context.gl.viewport( 0, 0, this.output.resolution.width, this.output.resolution.height );
this.context.gl.bindFramebuffer( this.context.gl.FRAMEBUFFER, this.output.framebuffer );
this.context.gl.clear( this.context.gl.COLOR_BUFFER_BIT );
this.context.gl.bindBuffer( this.context.vertexIndexBuffer.bufferTarget, this.context.vertexIndexBuffer );
this.context.gl.drawElements( this.context.gl.TRIANGLE_STRIP, this.context.vertexIndexBuffer.itemCount, this.context.gl.UNSIGNED_SHORT, 0 );
this.next && this.next.render( );
}
}
RenderPass.EntryPoint = class extends RenderPass {
constructor( { context } ) {
super( );
this.context = context;
this.output = new RenderOutput( );
this.output.texture = this.context.createTexture( );
this.setInputSize( 0, 0 );
}
setInputSize( width, height ) {
this.output.resolution.width = width;
this.output.resolution.height = height;
this.context.setTextureSize( this.output.texture, this.output.resolution.width, this.output.resolution.height );
this.next && this.next.refreshInputs( );
}
setInputData( data ) {
let format = this.context.gl.RGBA;
let type = this.context.gl.UNSIGNED_BYTE;
this.context.gl.bindTexture( this.context.gl.TEXTURE_2D, this.output.texture );
this.context.gl.texImage2D( this.context.gl.TEXTURE_2D, 0, this.context.gl.RGBA, this.output.resolution.width, this.output.resolution.height, 0, format, type, data );
this.context.gl.bindTexture( this.context.gl.TEXTURE_2D, null );
}
refreshOutput( ) {
this.next && this.next.refreshInputs( );
}
getOutputs( ) {
return [ this.output ];
}
render( ) {
this.next && this.next.render( );
}
};
RenderPass.StandardOutput = class extends RenderPassShader {
constructor( { context }, parent ) {
super( { context, shader : new Shader( {
fragment : `
precision mediump float;
uniform sampler2D uInputSample;
varying vec2 vTextureCoordinates;
void main( void ) {
gl_FragColor = texture2D( uInputSample, vTextureCoordinates );
}
`,
vertex : `
precision mediump float;
uniform mat4 uMatrix;
attribute vec3 aVertexPosition;
attribute vec2 aVertexTextureUv;
varying vec2 vTextureCoordinates;
void main( void ) {
vTextureCoordinates = vec2( aVertexTextureUv.s, 1.0 - aVertexTextureUv.t );
gl_Position = uMatrix * vec4( aVertexPosition, 1.0 );
}
`
} ) }, parent );
this.outputWidth = 0;
this.outputHeight = 0;
}
setOutputSize( width, height ) {
this.outputWidth = width;
this.outputHeight = height;
this.refreshInputs( );
}
refreshInputs( ) {
super.refreshInputs( );
this.context.useProgram( this.program, ( ) => {
var inputWidth = this.inputs.length > 0 ? this.inputs[ 0 ].resolution.width : 0;
var inputHeight = this.inputs.length > 0 ? this.inputs[ 0 ].resolution.height : 0;
var outputWidth = this.output.resolution.width;
var outputHeight = this.output.resolution.height;
var isUndefined = value => value == null || value === '';
if ( isUndefined( outputWidth ) && isUndefined( outputHeight ) )
outputWidth = inputWidth, outputHeight = inputHeight;
if ( isUndefined( outputWidth ) )
outputWidth = inputWidth * ( outputHeight / inputHeight );
if ( isUndefined( outputHeight ) )
outputHeight = inputHeight * ( outputWidth / inputWidth );
var widthRatio = outputWidth / inputWidth;
var heightRatio = outputHeight / inputHeight;
var ratio = Math.min( widthRatio, heightRatio );
var viewportWidth = widthRatio / ratio;
var viewportHeight = heightRatio / ratio;
var matrix = this._createOrthoMatrix( - viewportWidth, viewportWidth, - viewportHeight, viewportHeight, - 100, 100 );
this.context.gl.uniformMatrix4fv( this.context.gl.getUniformLocation( this.program, 'uMatrix' ), false, matrix );
} );
}
getResolution( ) {
return { width : this.outputWidth, height : this.outputHeight };
}
_createOrthoMatrix( left, right, bottom, top, near, far ) {
var lr = 1 / ( left - right ), bt = 1 / ( bottom - top ), nf = 1 / ( near - far );
return [ - 2 * lr, 0, 0, 0, 0, - 2 * bt, 0, 0, 0, 0, 2 * nf, 0, ( left + right ) * lr, ( bottom + top ) * bt, ( near + far ) * nf, 1 ];
}
};
class RenderContext {
constructor( { gl } ) {
this.gl = gl;
this.vertexPositionBuffer = this.createBuffer( this.gl.ARRAY_BUFFER, 4, new Float32Array( [ -1, -1, 0, /**/ 1, -1, 0, /**/ 1, 1, 0, /**/ -1, 1, 0 ] ) );
this.vertexTextureUvBuffer = this.createBuffer( this.gl.ARRAY_BUFFER, 4, new Float32Array( [ 0, 0, /**/ 1, 0, /**/ 1, 1, /**/ 0, 1 ] ) );
this.vertexIndexBuffer = this.createBuffer( this.gl.ELEMENT_ARRAY_BUFFER, 4, new Uint16Array( [ 0, 1, 3, 2 ] ) );
}
createBuffer( target, count, content ) {
var buffer = this.gl.createBuffer( );
buffer.bufferTarget = target;
buffer.itemCount = count;
buffer.itemSize = content.length / count;
this.gl.bindBuffer( buffer.bufferTarget, buffer );
this.gl.bufferData( buffer.bufferTarget, content, this.gl.STATIC_DRAW );
this.gl.bindBuffer( buffer.bufferTarget, null );
return buffer;
}
createShader( type, script ) {
let shader = this.gl.createShader( type );
this.gl.shaderSource( shader, script );
this.gl.compileShader( shader );
if ( ! this.gl.getShaderParameter( shader, this.gl.COMPILE_STATUS ) )
throw new Error( `Shader compilation failed: ${this.gl.getShaderInfoLog(shader)}` );
return shader;
}
createProgram( fragment, vertex ) {
let program = this.gl.createProgram( );
this.gl.attachShader( program, vertex );
this.gl.attachShader( program, fragment );
this.gl.linkProgram( program );
if ( ! this.gl.getProgramParameter( program, this.gl.LINK_STATUS ) )
throw new Error( `Shader linking failed: ${this.gl.getError()}` );
return program;
}
createFramebuffer( ) {
let framebuffer = this.gl.createFramebuffer( );
return framebuffer;
}
createTexture( ) {
let texture = this.gl.createTexture( );
this.gl.bindTexture( this.gl.TEXTURE_2D, texture );
this.gl.texParameteri( this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE );
this.gl.texParameteri( this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE );
this.gl.texParameteri( this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST );
this.gl.texParameteri( this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST );
this.gl.bindTexture( this.gl.TEXTURE_2D, null );
return texture;
}
setTextureSize( texture, width, height ) {
this.gl.bindTexture( this.gl.TEXTURE_2D, texture );
this.gl.texImage2D( this.gl.TEXTURE_2D, 0, this.gl.RGBA, width, height, 0, this.gl.RGBA, this.gl.UNSIGNED_BYTE, null );
this.gl.bindTexture( this.gl.TEXTURE_2D, null );
}
useProgram( program, callback ) {
this.gl.useProgram( program );
callback( );
this.gl.useProgram( null );
}
}
function getPixelData( image ) {
let canvas = document.createElement( 'canvas' );
let context = canvas.getContext( '2d' );
canvas.width = image.width;
canvas.height = image.height;
context.drawImage( image, 0, 0 );
let canvasData = context.getImageData( 0, 0, canvas.width, canvas.height ).data;
let pixelData = new Uint8Array( canvasData.length );
for ( let t = 0; t < pixelData.length; ++ t )
pixelData[ t ] = canvasData[ t ];
return pixelData;
}
( function ( ) {
let image = new Image( );
image.crossOrigin = 'anonymous';
image.src = 'http://i.imgur.com/xUm3XMz.png';
image.addEventListener( 'load', ( ) => {
image.data = getPixelData( image );
let canvas = document.createElement( 'canvas' );
document.body.appendChild( canvas );
canvas.width = image.width * 1;
canvas.height = image.height * 1;
let gl = canvas.getContext( 'webgl' );
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
let context = new RenderContext( { gl } );
let entry = new RenderPass.EntryPoint( { context } ), pipeline = entry;
let output = new RenderPass.StandardOutput( { context } );
pipeline.append( output );
entry.setInputSize( image.width, image.height );
entry.setInputData( image.data );
output.setOutputSize( canvas.width, canvas.height );
var render = ( ) => {
requestAnimationFrame( render );
entry.render( );
};
render( );
} );
} )( );
答案 0 :(得分:0)
我忘了启用这些属性。以下代码修复了它:
this.context.gl.enableVertexAttribArray( this.aVertexPositionLocation )