尝试从帧缓冲区传递纹理会导致 L_INVALID_OPERATION:glDrawArrays:绘制的源纹理和目标纹理相同
Posted
技术标签:
【中文标题】尝试从帧缓冲区传递纹理会导致 L_INVALID_OPERATION:glDrawArrays:绘制的源纹理和目标纹理相同【英文标题】:Trying to pass texture from framebuffer results in L_INVALID_OPERATION : glDrawArrays: Source and destination textures of the draw are the same 【发布时间】:2019-10-25 21:55:44 【问题描述】:我是 vanilla WebGL 的新手,正在尝试将帧缓冲区用于后期处理/高级着色器。当我运行我的代码时,我收到警告:
GL_INVALID_OPERATION : glDrawArrays: 绘制的源纹理和目标纹理相同。
到目前为止,这是我的代码。如果有人能指出我正确的方向,如何正确利用帧缓冲区将纹理传递到下一个通道。它被包裹在一个 vue.js 组件中,但这并不重要。
<template lang='pug'>
canvas
</template>
<script>
import mapGetters from 'vuex'
export default
name: 'webGl',
created ()
this.static =
af: null,
gl: null,
fr: 0,
shaders:
vertex: `
attribute vec2 a_position;
void main()
gl_Position = vec4(a_position, 0, 1);
`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
uniform float u_time;
uniform vec2 u_size;
uniform int u_frame;
uniform sampler2D u_texture;
const int maxIter = 15;
vec2 getPos()
vec2 pos = ( gl_FragCoord.xy / u_size.xy ) - vec2(0.5);
pos.x *= u_size.x / u_size.y;
return pos;
vec2 cmult(vec2 a, vec2 b)
return vec2(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
float length2(vec2 v)
return v.x*v.x+v.y*v.y;
vec2 map(vec2 pos)
return pos;
return vec2(pos.x * sqrt(1.-pos.y*pos.y*3.), pos.y * sqrt(1.-pos.x*pos.x*2.));
vec2 iterate(vec2 p, vec2 c)
vec2 p2 = cmult(p,p);
return p2 + c;
bool checkAbort(vec2 p, vec2 c)
return length2(p) > 400.;
float l2 = log(2.);
vec4 defaultColor ( void )
return vec4(0.35,0.35,0.35,1.0);
vec4 color(int iterations, vec2 p)
float col = .20 + (float(iterations) - log(log(length2(p)))/l2) / float(maxIter);
return defaultColor() * vec4(col);
void main( void )
if (u_frame < 300)
vec2 c = map(getPos())*0.8 - vec2(0.5);
vec2 p = c + vec2(sin(-u_time), cos(u_time)) * 0.2;
float m;
for(int i = 0; i < maxIter ;i++)
p = iterate(p,c);
if(checkAbort(p,c))
gl_FragColor = color(i,p);
return;
gl_FragColor = defaultColor();
else
gl_FragColor = texture2D(u_texture, gl_FragCoord.xy / u_size.xy);
`,
program: null,
attributes: ,
uniforms: ,
time: 0
,
mounted ()
this.setInitWebGlContext()
this.setInitShaderProgram()
this.setInitAttributes(['a_position'])
this.setInitUniforms(['u_size', 'u_time', 'u_frame', 'u_texture'])
this.setInitGeometryBuffer()
this.setRenderLoop()
,
beforeDestroy ()
window.cancelAnimationFrame(this.static.af)
,
computed:
...mapGetters([
'getCalcs'
])
,
methods:
setInitWebGlContext ()
this.static.gl = this.$el.getContext('webgl')
if (this.static.gl === null)
console.log('Unable to initialize WebGL. Your browser or machine may not support it.')
,
setInitShaderProgram ()
const gl = this.static.gl
this.static.shaders.program = gl.createProgram()
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader, this.static.shaders.vertex)
gl.shaderSource(fragmentShader, this.static.shaders.fragment)
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
gl.attachShader(this.static.shaders.program, vertexShader)
gl.attachShader(this.static.shaders.program, fragmentShader)
gl.linkProgram(this.static.shaders.program)
gl.useProgram(this.static.shaders.program)
,
setInitAttributes (keys)
const gl = this.static.gl
const program = this.static.shaders.program
for (let i = 0; i < keys.length; i++)
this.static.shaders.attributes[keys[i]] = gl.getAttribLocation(program, keys[i])
,
setInitUniforms (keys)
const gl = this.static.gl
const program = this.static.shaders.program
for (let i = 0; i < keys.length; i++)
this.static.shaders.uniforms[keys[i]] = gl.getUniformLocation(program, keys[i])
,
setInitGeometryBuffer ()
const gl = this.static.gl
const buffer = gl.createBuffer()
gl.bindBuffer(this.static.gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]), gl.STATIC_DRAW)
,
setCreateTexture ()
const gl = this.static.gl
const width = this.getCalcs.vw
const height = this.getCalcs.vh
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
return texture
,
setCreateFramebuffer ()
const gl = this.static.gl
const buffer = gl.createFramebuffer()
gl.bindFramebuffer(gl.FRAMEBUFFER, buffer)
const texture = this.setCreateTexture()
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)
return
texture: texture,
buffer: buffer
,
setRenderLoop ()
this.static.af = window.requestAnimationFrame(this.setRenderLoop)
const gl = this.static.gl
const fb = this.static.fb
const width = this.getCalcs.vw
const height = this.getCalcs.vh
const attributes = this.static.shaders.attributes
const uniforms = this.static.shaders.uniforms
const mouse = this.static.shaders.mouse
const fr = this.static.fr
this.$el.width = width
this.$el.height = height
const bufferA = this.setCreateFramebuffer()
gl.viewport(0, 0, width, height)
gl.clearColor(0.0, 0.0, 0.0, 0.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
gl.uniform1f(uniforms.u_time, window.performance.now() / 3000)
gl.uniform1i(uniforms.u_frame, fr)
gl.drawArrays(gl.TRIANGLES, 0, 6)
gl.bindTexture(gl.TEXTURE_2D, bufferA.texture)
gl.bindFramebuffer(gl.FRAMEBUFFER, null)
gl.viewport(0, 0, width, height)
gl.clearColor(0.0, 0.0, 0.0, 0.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
gl.uniform1f(uniforms.u_time, window.performance.now() / 3000)
gl.uniform1i(uniforms.u_frame, fr)
gl.uniform1i(uniforms.u_texture, 0)
gl.drawArrays(gl.TRIANGLES, 0, 6)
this.static.fr++
</script>
我现在更进一步,下面的代码正在运行
<template lang='pug'>
canvas
</template>
<script>
import mapGetters from 'vuex'
import forEach from 'lodash/forEach'
export default
name: 'webGl',
created ()
this.static =
af: null,
gl: null,
fr: 0,
shaders:
noise:
vertex: `
attribute vec2 a_position;
void main()
gl_Position = vec4(a_position, 0, 1);
`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_size;
vec2 hash( vec2 p )
p = vec2( dot(p,vec2(127.1,311.7)),
dot(p,vec2(269.5,183.3)));
return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
float noise( vec2 p )
const float K1 = 0.366025404;
const float K2 = 0.211324865;
vec2 i = floor(p + (p.x + p.y) * K1);
vec2 a = p - i + (i.x + i.y) * K2;
vec2 o = step(a.yx, a.xy);
vec2 b = a - o + K2;
vec2 c = a - 1.0 + 2.0 * K2;
vec3 h = max(0.5 - vec3(dot(a,a), dot(b,b), dot(c,c)), 0.0);
vec3 n = h * h * h * h * vec3(dot(a, hash(i + 0.0)), dot(b, hash(i + o)), dot(c, hash(i + 1.0)));
return dot(n, vec3(70.0));
void main( void )
vec2 vUv = gl_FragCoord.xy / u_size.xy;
vec3 rnd = vec3(noise(16.0 * vUv + 1.1), noise(16.0 * vUv + 2.2), noise(16.0 * vUv + 3.3));
gl_FragColor = vec4(rnd, 1.0);
`,
program: null,
attributes:
a_position: null
,
uniforms:
u_size: null
,
fluid:
vertex: `
attribute vec2 a_position;
void main()
gl_Position = vec4(a_position, 0, 1);
`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_size;
uniform sampler2D u_image;
vec2 normz(vec2 x)
return x == vec2(0.0, 0.0) ? vec2(0.0, 0.0) : normalize(x);
vec3 advect(vec2 ab, vec2 vUv, vec2 step, float sc)
vec2 aUv = vUv - ab * sc * step;
const float _G0 = 0.25; // center weight
const float _G1 = 0.125; // edge-neighbors
const float _G2 = 0.0625; // vertex-neighbors
// 3x3 neighborhood coordinates
float step_x = step.x;
float step_y = step.y;
vec2 n = vec2(0.0, step_y);
vec2 ne = vec2(step_x, step_y);
vec2 e = vec2(step_x, 0.0);
vec2 se = vec2(step_x, -step_y);
vec2 s = vec2(0.0, -step_y);
vec2 sw = vec2(-step_x, -step_y);
vec2 w = vec2(-step_x, 0.0);
vec2 nw = vec2(-step_x, step_y);
vec3 uv = texture2D(u_image, fract(aUv)).xyz;
vec3 uv_n = texture2D(u_image, fract(aUv+n)).xyz;
vec3 uv_e = texture2D(u_image, fract(aUv+e)).xyz;
vec3 uv_s = texture2D(u_image, fract(aUv+s)).xyz;
vec3 uv_w = texture2D(u_image, fract(aUv+w)).xyz;
vec3 uv_nw = texture2D(u_image, fract(aUv+nw)).xyz;
vec3 uv_sw = texture2D(u_image, fract(aUv+sw)).xyz;
vec3 uv_ne = texture2D(u_image, fract(aUv+ne)).xyz;
vec3 uv_se = texture2D(u_image, fract(aUv+se)).xyz;
return _G0*uv + _G1*(uv_n + uv_e + uv_w + uv_s) + _G2*(uv_nw + uv_sw + uv_ne + uv_se);
void main( void )
const float _K0 = -20.0/6.0; // center weight
const float _K1 = 4.0/6.0; // edge-neighbors
const float _K2 = 1.0/6.0; // vertex-neighbors
const float cs = -0.6; // curl scale
const float ls = 0.05; // laplacian scale
const float ps = -0.8; // laplacian of divergence scale
const float ds = -0.05; // divergence scale
const float dp = -0.04; // divergence update scale
const float pl = 0.3; // divergence smoothing
const float ad = 6.0; // advection distance scale
const float pwr = 1.0; // power when deriving rotation angle from curl
const float amp = 1.0; // self-amplification
const float upd = 0.8; // update smoothing
const float sq2 = 0.6; // diagonal weight
vec2 vUv = gl_FragCoord.xy / u_size.xy;
vec2 texel = 1. / u_size.xy;
float step_x = texel.x;
float step_y = texel.y;
vec2 n = vec2(0.0, step_y);
vec2 ne = vec2(step_x, step_y);
vec2 e = vec2(step_x, 0.0);
vec2 se = vec2(step_x, -step_y);
vec2 s = vec2(0.0, -step_y);
vec2 sw = vec2(-step_x, -step_y);
vec2 w = vec2(-step_x, 0.0);
vec2 nw = vec2(-step_x, step_y);
vec3 uv = texture2D(u_image, fract(vUv)).xyz;
vec3 uv_n = texture2D(u_image, fract(vUv+n)).xyz;
vec3 uv_e = texture2D(u_image, fract(vUv+e)).xyz;
vec3 uv_s = texture2D(u_image, fract(vUv+s)).xyz;
vec3 uv_w = texture2D(u_image, fract(vUv+w)).xyz;
vec3 uv_nw = texture2D(u_image, fract(vUv+nw)).xyz;
vec3 uv_sw = texture2D(u_image, fract(vUv+sw)).xyz;
vec3 uv_ne = texture2D(u_image, fract(vUv+ne)).xyz;
vec3 uv_se = texture2D(u_image, fract(vUv+se)).xyz;
vec3 lapl = _K0*uv + _K1*(uv_n + uv_e + uv_w + uv_s) + _K2*(uv_nw + uv_sw + uv_ne + uv_se);
float sp = ps * lapl.z;
float curl = uv_n.x - uv_s.x - uv_e.y + uv_w.y + sq2 * (uv_nw.x + uv_nw.y + uv_ne.x - uv_ne.y + uv_sw.y - uv_sw.x - uv_se.y - uv_se.x);
float sc = cs * sign(curl) * pow(abs(curl), pwr);
float div = uv_s.y - uv_n.y - uv_e.x + uv_w.x + sq2 * (uv_nw.x - uv_nw.y - uv_ne.x - uv_ne.y + uv_sw.x + uv_sw.y + uv_se.y - uv_se.x);
float sd = uv.z + dp * div + pl * lapl.z;
vec2 norm = normz(uv.xy);
vec3 ab = advect(vec2(uv.x, uv.y), vUv, texel, ad);
float ta = amp * ab.x + ls * lapl.x + norm.x * sp + uv.x * ds * sd;
float tb = amp * ab.y + ls * lapl.y + norm.y * sp + uv.y * ds * sd;
float a = ta * cos(sc) - tb * sin(sc);
float b = ta * sin(sc) + tb * cos(sc);
vec3 abd = upd * uv + (1.0 - upd) * vec3(a,b,sd);
abd.z = clamp(abd.z, -1.0, 1.0);
abd.xy = clamp(length(abd.xy) > 1.0 ? normz(abd.xy) : abd.xy, -1.0, 1.0);
gl_FragColor = vec4(abd, 0.0);
`,
program: null,
attributes:
a_position: null
,
uniforms:
u_size: null
,
colorize:
vertex: `
attribute vec2 a_position;
void main()
gl_Position = vec4(a_position, 0, 1);
`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_size;
uniform sampler2D u_image;
void main( void )
vec2 texel = 1. / u_size.xy;
vec2 uv = gl_FragCoord.xy / u_size.xy;
vec3 c = texture2D(u_image, uv).xyz;
vec3 norm = normalize(c);
vec3 div = vec3(0.1) * norm.z;
vec3 rbcol = 0.5 + 0.6 * cross(norm.xyz, vec3(0.5, -0.4, 0.5));
gl_FragColor = vec4(rbcol + div, 1.0);
`,
program: null,
attributes:
a_position: null
,
uniforms:
u_size: null
,
textures:
default: null
,
mounted ()
this.setInitWebGlContext()
this.setInitGeometryBuffer()
this.setInitShaderPrograms()
this.setRenderLoop()
,
beforeDestroy ()
window.cancelAnimationFrame(this.static.af)
,
computed:
...mapGetters([
'getCalcs'
])
,
methods:
setInitWebGlContext ()
this.static.gl = this.$el.getContext('webgl')
if (this.static.gl === null)
console.log('Unable to initialize WebGL. Your browser or machine may not support it.')
,
setInitShaderPrograms ()
const gl = this.static.gl
forEach(this.static.shaders, shader =>
shader.program = gl.createProgram()
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader, shader.vertex)
gl.shaderSource(fragmentShader, shader.fragment)
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
gl.attachShader(shader.program, vertexShader)
gl.attachShader(shader.program, fragmentShader)
gl.linkProgram(shader.program)
)
,
setInitGeometryBuffer ()
const gl = this.static.gl
const buffer = gl.createBuffer()
gl.bindBuffer(this.static.gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]), gl.STATIC_DRAW)
,
setDraw (width, height)
const gl = this.static.gl
gl.clearColor(0.0, 0.0, 0.0, 0.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.viewport(0, 0, width, height)
gl.drawArrays(gl.TRIANGLES, 0, 6)
,
setProgram (shader)
const gl = this.static.gl
const program = this.static.shaders[shader].program
const attributes = this.static.shaders[shader].attributes
const uniforms = this.static.shaders[shader].uniforms
gl.useProgram(program)
forEach(attributes, (attribute, key) =>
attributes[key] = gl.getAttribLocation(program, key)
)
forEach(uniforms, (uniform, key) =>
uniforms[key] = gl.getUniformLocation(program, key)
)
,
setFrameBuffer (width, height)
const gl = this.static.gl
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
const framebuffer = gl.createFramebuffer()
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)
return
frameBuffer: framebuffer,
texture: texture
,
setNoise (width, height)
const gl = this.static.gl
const attributes = this.static.shaders.noise.attributes
const uniforms = this.static.shaders.noise.uniforms
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
,
setFluid (width, height)
const gl = this.static.gl
const attributes = this.static.shaders.fluid.attributes
const uniforms = this.static.shaders.fluid.uniforms
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
,
setColorize (width, height)
const gl = this.static.gl
const attributes = this.static.shaders.colorize.attributes
const uniforms = this.static.shaders.colorize.uniforms
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
,
setRenderLoop ()
this.static.af = window.requestAnimationFrame(this.setRenderLoop)
const gl = this.static.gl
const width = this.getCalcs.vw
const height = this.getCalcs.vh
this.$el.width = width
this.$el.height = height
if (!this.static.fr)
const noiseBuffer = this.setFrameBuffer(width, height)
this.setProgram('noise')
this.setNoise(width, height)
this.setDraw(width, height)
this.static.textures.default = noiseBuffer.texture
const fluidBuffer = this.setFrameBuffer(width, height)
gl.bindTexture(gl.TEXTURE_2D, this.static.textures.default)
this.setProgram('fluid')
this.setFluid(width, height)
this.setDraw(width, height)
this.static.textures.default = fluidBuffer.texture
gl.bindFramebuffer(gl.FRAMEBUFFER, null)
this.setProgram('colorize')
this.setColorize(width, height)
this.setDraw(width, height)
this.static.fr++
</script>
但是我正在尝试采用来自 shadertoy https://www.shadertoy.com/view/XddSRX 的着色器,如果我朗读我的代码,它的行为会非常不同。
【问题讨论】:
【参考方案1】:问题与错误中所述完全相同。
绘制的源纹理和目标纹理相同。
查看您的代码,有一个着色器,它引用一个纹理,有一个纹理,它附加到帧缓冲区,并且默认绑定到纹理单元 0。因此,当您绘制它时,它同时用作输入 (u_texture
) 和输出(当前帧缓冲区)。这是不允许的。
简单的解决方案是您需要另一个纹理。绘制到帧缓冲区时绑定该纹理。
更好的解决方案是您需要 2 个不同的着色器程序。一个用于绘制到不使用纹理作为输入的帧缓冲区,另一个用于绘制到画布。因为你有一个在u_frame
上分支的着色器。删除该分支并将事物分成 2 个着色器程序。一个计算颜色然后 u_frame
一些可能有用也可能没用的链接:drawing multiple things、render targets、image processing。
【讨论】:
以上是关于尝试从帧缓冲区传递纹理会导致 L_INVALID_OPERATION:glDrawArrays:绘制的源纹理和目标纹理相同的主要内容,如果未能解决你的问题,请参考以下文章
在 iOS 上的 OpenGL ES 着色器中混合多个纹理会导致反向行为