无法渲染到帧缓冲区(纹理)
Unable to render to framebuffer (texture)
我正在尝试使用阴影贴图实现阴影,因此我需要将场景渲染到单独的帧缓冲区(纹理)。我无法让它正常工作,所以在剥离我的代码库后,我只剩下一组相对简单的指令,这些指令应该将场景渲染为纹理,然后简单地渲染纹理。
该程序由两个程序组成:
- 地面程序
- 茶壶计划
第一个应该渲染一个具有特定纹理的矩形。第二个应该渲染一个茶壶(根据其位置使用颜色)。 Eech 渲染步骤执行以下操作(好吧,无论如何就是这个想法):
- 切换到帧缓冲区
- 渲染茶壶
- 切换到普通缓冲区
- 渲染茶壶
- 渲染地面
现在,地面片段着色器看起来像:
gl_FragColor = texture2D(shadowMap, fTexCoord);
'shadowMap' 是我在第 2 步中渲染的纹理。我 期望 看到一个漂浮的茶壶,下面画了一个矩形。这确实有效。现在,我还希望 'ground' 包含一个茶壶。毕竟,我们在没有地面的情况下渲染了我们正在看的场景到 framebuffer/texture.
代码
var UNSIGNED_SHORT_SIZE = 2;
// Variables filled by setup()
var glCanvas;
var gl, teapotProgram, groundProgram;
var vBuffer, iBuffer, fBuffer;
var vertices, indices, textures;
var teapot = null;
var model;
var view;
var light;
var projection;
var BASE_URL = "https://hmbastiaan.nl/martijn/webgl/W08P02_SO/";
var WIDTH = 150, HEIGHT = 150;
function makeTeapot(){
var drawingInfo = teapot.getDrawingInfoObjects();
var indices = drawingInfo.indices;
for(var i=0; i < indices.length; i++){
indices[i] += 4; // Add offset for 'ground'
}
return {
indices: drawingInfo.indices,
vertices: drawingInfo.vertices
}
}
function makeRectangle(x1, x2, y1, y2, z1, z2){
var x1 = -2,
x2 = 2,
y1 = -1,
y2 = -1,
z1 = -1,
z2 = -5;
var vertices = [
vec4(x1, y2, z1, 1),
vec4(x2, y1, z1, 1),
vec4(x2, y1, z2, 1),
vec4(x1, y2, z2, 1)
];
var textures = [
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
vec2( 1.0, 1.0),
vec2(-1.0, 1.0)
];
var indices = [
0, 1, 2,
0, 2, 3
];
return {
indices: indices,
vertices: vertices,
textures: textures
}
}
function resetBuffers(){
vertices = [];
indices = [];
textures = [];
// Add rectangle
var rectangle = makeRectangle();
Array.prototype.push.apply(vertices, rectangle.vertices);
Array.prototype.push.apply(indices, rectangle.indices);
Array.prototype.push.apply(textures, rectangle.textures);
// Add teapot
var teapot = makeTeapot();
Array.prototype.push.apply(vertices, teapot.vertices);
Array.prototype.push.apply(indices, teapot.indices);
console.log(vertices);
console.log(indices);
console.log(textures);
// Send to GPU
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
}
function setup(){
$.get(BASE_URL + "teapot.obj", function(teapot_obj_data){
teapot = new OBJDoc(BASE_URL + "teapot.obj");
if(!teapot.parse(teapot_obj_data, 1)){
alert("Parsing teapot.obj failed.");
return;
}
setup2();
}).fail(function(){
alert("Getting teapot.obj failed.");
});
}
function setup2(){
glCanvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(glCanvas, {stencil: true, alpha: false});
gl.viewport(0, 0, WIDTH, HEIGHT);
teapotProgram = initShaders(gl, BASE_URL + "vshader-teapot.glsl", BASE_URL + "fshader-teapot.glsl");
groundProgram = initShaders(gl, BASE_URL + "vshader-ground.glsl", BASE_URL + "fshader-ground.glsl");
light = vec3(0.0, 2.0, -2.0);
view = lookAt(vec3(0, 0, 3), vec3(0,0,0), vec3(0,1,0));
projection = perspective(45, 1.0, 1, 100.0);
// Get teapot uniforms
gl.useProgram(teapotProgram);
teapotProgram.modelLoc = gl.getUniformLocation(teapotProgram, "Model");
teapotProgram.viewLoc = gl.getUniformLocation(teapotProgram, "View");
teapotProgram.projectionLoc = gl.getUniformLocation(teapotProgram, "Projection");
// Upload uniforms
gl.uniformMatrix4fv(teapotProgram.projectionLoc, false, flatten(projection));
gl.uniformMatrix4fv(teapotProgram.viewLoc, false, flatten(view));
gl.uniformMatrix4fv(teapotProgram.modelLoc, false, flatten(scalem(0.25, 0.25, 0.25)));
// Get teapot attributes
teapotProgram.vPosition = gl.getAttribLocation(teapotProgram, "vPosition");
// Get ground uniforms
gl.useProgram(groundProgram);
groundProgram.modelLoc = gl.getUniformLocation(groundProgram, "Model");
groundProgram.viewLoc = gl.getUniformLocation(groundProgram, "View");
groundProgram.projectionLoc = gl.getUniformLocation(groundProgram, "Projection");
groundProgram.shadowMap = gl.getUniformLocation(groundProgram, "shadowMap");
// Get ground attributes
groundProgram.vTexCoord = gl.getAttribLocation(groundProgram, "vTexCoord");
groundProgram.vPosition = gl.getAttribLocation(groundProgram, "vPosition");
// Allocate and fill vertices buffer
vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.vertexAttribPointer(teapotProgram.vPosition, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(teapotProgram.vPosition);
gl.vertexAttribPointer(groundProgram.vPosition, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(groundProgram.vPosition);
// Allocate indices buffer
iBuffer = gl.createBuffer();
// Setup FBO
fBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fBuffer);
fBuffer.renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, fBuffer.renderbuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 512, 512);
fBuffer.texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 512, 512, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fBuffer.texture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, fBuffer.renderbuffer);
// Sanity checking: framebuffer seems to throw now errors
if (!gl.isFramebuffer(fBuffer)) {
throw("Invalid framebuffer");
}
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
switch (status) {
case gl.FRAMEBUFFER_COMPLETE:
break;
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
break;
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
break;
case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
break;
case gl.FRAMEBUFFER_UNSUPPORTED:
throw("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED");
break;
default:
throw("Incomplete framebuffer: " + status);
}
// Set ground textures
gl.uniform1i(groundProgram.shadowMap, 0);
// Upload uniforms
gl.uniformMatrix4fv(groundProgram.projectionLoc, false, flatten(projection));
gl.uniformMatrix4fv(groundProgram.viewLoc, false, flatten(view));
gl.uniformMatrix4fv(groundProgram.modelLoc, false, flatten(mat4()));
// Restore default buffers
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// Set background colour
gl.clearColor(0.3921, 0.5843, 0.9294, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
resetBuffers();
window.requestAnimationFrame(render);
}
function render(){
var teapot = makeTeapot();
gl.useProgram(teapotProgram);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
// Switch to framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, fBuffer);
// Draw teapot
teapot = makeTeapot();
gl.drawElements(gl.TRIANGLES, teapot.indices.length, gl.UNSIGNED_SHORT, 6 * UNSIGNED_SHORT_SIZE);
// Set framebuffer to defualt buffer (in-browser output)
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// Draw ground
gl.useProgram(groundProgram);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
// Render teapot
gl.useProgram(teapotProgram);
gl.drawElements(gl.TRIANGLES, teapot.indices.length, gl.UNSIGNED_SHORT, 6 * UNSIGNED_SHORT_SIZE);
}
setup();
<div>
<br/>
<canvas width="150" height="150" id="gl-canvas">Sorry :|</canvas>
</div>
<script type='text/javascript' src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/webgl-utils.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/initShaders2.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/MV.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/objParser.js"></script>
感兴趣的函数:
- setup2(): 设置所有缓冲区和制服。
- render(): 渲染场景。
免责声明:这是一个作业,尽管这段代码已经足够简化,看起来根本不像原来的作业:)。
一眼看去有几个问题
纹理绑定是全局的。由于在 setup2 中您取消绑定 1 纹理,这意味着它从未被使用过。
您需要在每次绘制调用之前绑定所需的任何纹理。换句话说,当你绘制地面时,你需要绑定茶壶纹理,如
gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
注意:这是对真正需要的东西的过度简化。你真的需要
选择一个纹理单元来绑定纹理
var unit = 5;
gl.activeTexture(gl.TEXTURE0 + unit);
将纹理绑定到该单元。
gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
将统一采样器设置为该纹理单元
gl.uniform1i(groundProgram.shadowMap, unit);
您不需要这些额外步骤的原因是 (a) 您只
有 1 个纹理,所以您使用纹理单元 #0,默认值和 (b) 因为
制服默认为 0,因此 shadowMap
正在查看纹理单元 #0。
因为您制作了一个 mipmapped 纹理,只是渲染到级别 0 不会更新 mips。
换句话说,在渲染茶壶后,您将拥有一个 mip 级别 0 的茶壶,但 mip 级别 1、2、3、4、5 等仍然没有任何内容。您需要致电
gl.generateMipmap(gl.TEXTURE_2D)
在将茶壶渲染到该纹理后的纹理。或者停止使用 mips
每次调用都需要设置viewport gl.bindFramebuffer
.
gl.bindFramebuffer
之后几乎总是会调用 gl.viewport
以使视口与您要渲染的对象的大小相匹配
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
// set to size of fb
gl.viewport(0, 0, widthOfFb, heightOfFb);
renderSomething();
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// set to size of canvas's drawingBuffer
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
属性设置是全局的
您设置了茶壶属性。然后你在纹理上画一个茶壶。然后绘制地面,但您仍在使用茶壶属性。
就像纹理一样,您需要在每次绘制调用之前设置属性。
我猜你真的不应该在你的渲染函数中调用 makeTeapot
而应该在设置中调用它。
您可能会发现 this article useful
您还应该考虑 not putting properties on WebGL objects as it's arguably an anti-pattern。
同步 XHR 请求也不酷。您在 JavaScript 控制台
中收到此消息
Synchronous XMLHttpRequest on the main thread is deprecated because
of its detrimental effects to the end user's experience. For more
help, check http://xhr.spec.whatwg.org/.
我正在尝试使用阴影贴图实现阴影,因此我需要将场景渲染到单独的帧缓冲区(纹理)。我无法让它正常工作,所以在剥离我的代码库后,我只剩下一组相对简单的指令,这些指令应该将场景渲染为纹理,然后简单地渲染纹理。
该程序由两个程序组成:
- 地面程序
- 茶壶计划
第一个应该渲染一个具有特定纹理的矩形。第二个应该渲染一个茶壶(根据其位置使用颜色)。 Eech 渲染步骤执行以下操作(好吧,无论如何就是这个想法):
- 切换到帧缓冲区
- 渲染茶壶
- 切换到普通缓冲区
- 渲染茶壶
- 渲染地面
现在,地面片段着色器看起来像:
gl_FragColor = texture2D(shadowMap, fTexCoord);
'shadowMap' 是我在第 2 步中渲染的纹理。我 期望 看到一个漂浮的茶壶,下面画了一个矩形。这确实有效。现在,我还希望 'ground' 包含一个茶壶。毕竟,我们在没有地面的情况下渲染了我们正在看的场景到 framebuffer/texture.
代码
var UNSIGNED_SHORT_SIZE = 2;
// Variables filled by setup()
var glCanvas;
var gl, teapotProgram, groundProgram;
var vBuffer, iBuffer, fBuffer;
var vertices, indices, textures;
var teapot = null;
var model;
var view;
var light;
var projection;
var BASE_URL = "https://hmbastiaan.nl/martijn/webgl/W08P02_SO/";
var WIDTH = 150, HEIGHT = 150;
function makeTeapot(){
var drawingInfo = teapot.getDrawingInfoObjects();
var indices = drawingInfo.indices;
for(var i=0; i < indices.length; i++){
indices[i] += 4; // Add offset for 'ground'
}
return {
indices: drawingInfo.indices,
vertices: drawingInfo.vertices
}
}
function makeRectangle(x1, x2, y1, y2, z1, z2){
var x1 = -2,
x2 = 2,
y1 = -1,
y2 = -1,
z1 = -1,
z2 = -5;
var vertices = [
vec4(x1, y2, z1, 1),
vec4(x2, y1, z1, 1),
vec4(x2, y1, z2, 1),
vec4(x1, y2, z2, 1)
];
var textures = [
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
vec2( 1.0, 1.0),
vec2(-1.0, 1.0)
];
var indices = [
0, 1, 2,
0, 2, 3
];
return {
indices: indices,
vertices: vertices,
textures: textures
}
}
function resetBuffers(){
vertices = [];
indices = [];
textures = [];
// Add rectangle
var rectangle = makeRectangle();
Array.prototype.push.apply(vertices, rectangle.vertices);
Array.prototype.push.apply(indices, rectangle.indices);
Array.prototype.push.apply(textures, rectangle.textures);
// Add teapot
var teapot = makeTeapot();
Array.prototype.push.apply(vertices, teapot.vertices);
Array.prototype.push.apply(indices, teapot.indices);
console.log(vertices);
console.log(indices);
console.log(textures);
// Send to GPU
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
}
function setup(){
$.get(BASE_URL + "teapot.obj", function(teapot_obj_data){
teapot = new OBJDoc(BASE_URL + "teapot.obj");
if(!teapot.parse(teapot_obj_data, 1)){
alert("Parsing teapot.obj failed.");
return;
}
setup2();
}).fail(function(){
alert("Getting teapot.obj failed.");
});
}
function setup2(){
glCanvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(glCanvas, {stencil: true, alpha: false});
gl.viewport(0, 0, WIDTH, HEIGHT);
teapotProgram = initShaders(gl, BASE_URL + "vshader-teapot.glsl", BASE_URL + "fshader-teapot.glsl");
groundProgram = initShaders(gl, BASE_URL + "vshader-ground.glsl", BASE_URL + "fshader-ground.glsl");
light = vec3(0.0, 2.0, -2.0);
view = lookAt(vec3(0, 0, 3), vec3(0,0,0), vec3(0,1,0));
projection = perspective(45, 1.0, 1, 100.0);
// Get teapot uniforms
gl.useProgram(teapotProgram);
teapotProgram.modelLoc = gl.getUniformLocation(teapotProgram, "Model");
teapotProgram.viewLoc = gl.getUniformLocation(teapotProgram, "View");
teapotProgram.projectionLoc = gl.getUniformLocation(teapotProgram, "Projection");
// Upload uniforms
gl.uniformMatrix4fv(teapotProgram.projectionLoc, false, flatten(projection));
gl.uniformMatrix4fv(teapotProgram.viewLoc, false, flatten(view));
gl.uniformMatrix4fv(teapotProgram.modelLoc, false, flatten(scalem(0.25, 0.25, 0.25)));
// Get teapot attributes
teapotProgram.vPosition = gl.getAttribLocation(teapotProgram, "vPosition");
// Get ground uniforms
gl.useProgram(groundProgram);
groundProgram.modelLoc = gl.getUniformLocation(groundProgram, "Model");
groundProgram.viewLoc = gl.getUniformLocation(groundProgram, "View");
groundProgram.projectionLoc = gl.getUniformLocation(groundProgram, "Projection");
groundProgram.shadowMap = gl.getUniformLocation(groundProgram, "shadowMap");
// Get ground attributes
groundProgram.vTexCoord = gl.getAttribLocation(groundProgram, "vTexCoord");
groundProgram.vPosition = gl.getAttribLocation(groundProgram, "vPosition");
// Allocate and fill vertices buffer
vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.vertexAttribPointer(teapotProgram.vPosition, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(teapotProgram.vPosition);
gl.vertexAttribPointer(groundProgram.vPosition, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(groundProgram.vPosition);
// Allocate indices buffer
iBuffer = gl.createBuffer();
// Setup FBO
fBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fBuffer);
fBuffer.renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, fBuffer.renderbuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 512, 512);
fBuffer.texture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 512, 512, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fBuffer.texture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, fBuffer.renderbuffer);
// Sanity checking: framebuffer seems to throw now errors
if (!gl.isFramebuffer(fBuffer)) {
throw("Invalid framebuffer");
}
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
switch (status) {
case gl.FRAMEBUFFER_COMPLETE:
break;
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
break;
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
break;
case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
break;
case gl.FRAMEBUFFER_UNSUPPORTED:
throw("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED");
break;
default:
throw("Incomplete framebuffer: " + status);
}
// Set ground textures
gl.uniform1i(groundProgram.shadowMap, 0);
// Upload uniforms
gl.uniformMatrix4fv(groundProgram.projectionLoc, false, flatten(projection));
gl.uniformMatrix4fv(groundProgram.viewLoc, false, flatten(view));
gl.uniformMatrix4fv(groundProgram.modelLoc, false, flatten(mat4()));
// Restore default buffers
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// Set background colour
gl.clearColor(0.3921, 0.5843, 0.9294, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
resetBuffers();
window.requestAnimationFrame(render);
}
function render(){
var teapot = makeTeapot();
gl.useProgram(teapotProgram);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
// Switch to framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, fBuffer);
// Draw teapot
teapot = makeTeapot();
gl.drawElements(gl.TRIANGLES, teapot.indices.length, gl.UNSIGNED_SHORT, 6 * UNSIGNED_SHORT_SIZE);
// Set framebuffer to defualt buffer (in-browser output)
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// Draw ground
gl.useProgram(groundProgram);
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
// Render teapot
gl.useProgram(teapotProgram);
gl.drawElements(gl.TRIANGLES, teapot.indices.length, gl.UNSIGNED_SHORT, 6 * UNSIGNED_SHORT_SIZE);
}
setup();
<div>
<br/>
<canvas width="150" height="150" id="gl-canvas">Sorry :|</canvas>
</div>
<script type='text/javascript' src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/webgl-utils.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/initShaders2.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/MV.js"></script>
<script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/objParser.js"></script>
感兴趣的函数:
- setup2(): 设置所有缓冲区和制服。
- render(): 渲染场景。
免责声明:这是一个作业,尽管这段代码已经足够简化,看起来根本不像原来的作业:)。
一眼看去有几个问题
纹理绑定是全局的。由于在 setup2 中您取消绑定 1 纹理,这意味着它从未被使用过。
您需要在每次绘制调用之前绑定所需的任何纹理。换句话说,当你绘制地面时,你需要绑定茶壶纹理,如
gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
注意:这是对真正需要的东西的过度简化。你真的需要
选择一个纹理单元来绑定纹理
var unit = 5; gl.activeTexture(gl.TEXTURE0 + unit);
将纹理绑定到该单元。
gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
将统一采样器设置为该纹理单元
gl.uniform1i(groundProgram.shadowMap, unit);
您不需要这些额外步骤的原因是 (a) 您只 有 1 个纹理,所以您使用纹理单元 #0,默认值和 (b) 因为 制服默认为 0,因此
shadowMap
正在查看纹理单元 #0。因为您制作了一个 mipmapped 纹理,只是渲染到级别 0 不会更新 mips。
换句话说,在渲染茶壶后,您将拥有一个 mip 级别 0 的茶壶,但 mip 级别 1、2、3、4、5 等仍然没有任何内容。您需要致电
gl.generateMipmap(gl.TEXTURE_2D)
在将茶壶渲染到该纹理后的纹理。或者停止使用 mips
每次调用都需要设置viewport
gl.bindFramebuffer
.gl.bindFramebuffer
之后几乎总是会调用gl.viewport
以使视口与您要渲染的对象的大小相匹配gl.bindFramebuffer(gl.FRAMEBUFFER, fb); // set to size of fb gl.viewport(0, 0, widthOfFb, heightOfFb); renderSomething(); gl.bindFramebuffer(gl.FRAMEBUFFER, null); // set to size of canvas's drawingBuffer gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
属性设置是全局的
您设置了茶壶属性。然后你在纹理上画一个茶壶。然后绘制地面,但您仍在使用茶壶属性。
就像纹理一样,您需要在每次绘制调用之前设置属性。
我猜你真的不应该在你的渲染函数中调用 makeTeapot
而应该在设置中调用它。
您可能会发现 this article useful
您还应该考虑 not putting properties on WebGL objects as it's arguably an anti-pattern。
同步 XHR 请求也不酷。您在 JavaScript 控制台
中收到此消息Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/.