0
我正在爲我的項目構建多通道渲染器。事情是,我甚至不能顯示一張照片。我用WebGL Inspector查看,但一切似乎都很好(我檢查了一個更簡單的版本,我知道它正在工作,而且一切看起來都差不多)。無法用着色器顯示圖片
[編輯]這裏是一個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();
});
})();