WebGL-11.渲染到纹理

本系列教程主要讲解利用WebGL开发网页版的三维图形程序。这里主要用到的OpenGL和FlyMath内容。本系列内容较难。要求学生对几何和编程有一定的了解。建议初三以上同学学习。

离屏渲染也叫渲染到纹理。也就FBO(帧缓冲对象)其实就是两次渲染。第一次渲染的图像保存在一张纹理上,第二次渲染时把第一次渲染出来的纹理使用上。第一次的渲染结果只保存在纹理上,不在屏幕上显示。所以叫“离屏渲染”。

离屏渲染分为8个部份:

  1. 创建帧缓冲区对象。
  2. 创建纹理对象,并设置其尺寸。一般与第二次渲染时使用的纹理大小相同。
  3. 创建洝缓冲区对象。
  4. 绑定渲染缓冲区对象,并设置尺寸。大小和纹理大小相对。
  5. 将纹理对象关联到帧缓冲区对象。
  6. 将渲染缓冲区对象关联到帧缓冲区对象。
  7. 检查帧缓冲区的配置。
  8. 开始在帧缓冲区进行绘图渲染。

离屏渲染主要用在镜子和阴影上。比如一个镜子,镜子前方有一物体。摄像机在物体后方看向镜子。应该看到真实的物体和镜子里的物体背影。渲染过程是这样的:1、在摄像机的对称方向(镜子里面)看向物体(这时不渲染镜子,只渲染物体)。把渲染到的物体图像存在纹理里。2、摄像机复位,渲染得到的纹理,应用在镜子上面。这时渲染时来的镜子就有了物体的背影图。

渲染这节的例子是这样的:先把一个红色的三角形渲染到纹理。然后把这张纹理用在一个正方形上。渲染三角形时把背景颜色调为(0,0,0.3),比后面的淡一些,以示区别。


我们继续在上节的例子上面修改。给正方形加上一张纹理图片。

javascript部分源码

//定义全局GL上下文
var gl_context;
var canvas;
     
//循环控制参数
var curTime;
var isStop=1;
    
//定义Camera位置
var camera = [-0.1,0,1];
var camera_to = [0,0,0];
var camera_up = [0,1,0];
     
//定义颜色数组
var tri_color = [1,0,0];
var quad_color = [1,1,0];
     
     
//定义camera矩阵和工程矩阵
var matView = new Array;
var matProject = new Array;
    
//定义离屏缓冲区参数
var office_width = 512;
var office_height = 512;
var framebuffer;
var frame_texture;
var depthbuffer;
    
//定义引用shader中的参数
var shader_pos;
var shader_uv;
var ColorUniform;
var shaderModelViewMatrixUniform;
var shaderProjectionMatrixUniform;
var SamplerUniform;
    
//定义顶点shader和片断shader
var vs_tri_src = "attribute vec3 a_Position;\n" +
         "uniform mat4 projectionMatrix;\n" +
         "uniform mat4 modelViewMatrix;\n" +
         "uniform vec3 a_color;\n" +
         "varying vec3 A_color;\n" +
         "void main() {\n" +
                "gl_Position = projectionMatrix * modelViewMatrix * vec4(a_Position,1);\n"+
                "A_color = a_color;\n"+                 
                "}\n";
     
var fs_tri_src = "precision highp float;\n" +             
         "varying vec3 A_color;\n" +              
         "void main() {\n" +           
         " gl_FragColor = vec4(A_color.xyz,1);\n" +
         "}\n";
             
var vs_quad_src = "attribute vec4 a_Position;\n" +
                  "attribute vec2 a_UV;\n" +
                  "uniform mat4 projectionMatrix;\n" +
                  "uniform mat4 modelViewMatrix;\n" +
                  "varying vec2 A_UV;\n" +               
                  "void main() {\n" +
                    "gl_Position = projectionMatrix * modelViewMatrix * a_Position;\n"+
                    "A_UV = a_UV;" +
                    "}\n";
     
var fs_quad_src = "precision highp float;\n" +
             "varying vec2 A_UV;\n" +          
             "uniform sampler2D A_Texture;\n" +
             "void main() {\n" +
                " vec4 imgColor = texture2D(A_Texture,A_UV);\n" +
                " gl_FragColor = imgColor;\n" +
                "}\n";
    
    
//初始离屏渲染缓冲区
function initFrameBuffer()
{
    framebuffer = gl_context.createFramebuffer();
    framebuffer.width = office_width;
    framebuffer.height = office_height;
        
    //创建帧缓冲区,保存颜色信息
    frame_texture = gl_context.createTexture();
    gl_context.bindTexture(gl_context.TEXTURE_2D,frame_texture);
    gl_context.texParameteri(gl_context.TEXTURE_2D, gl_context.TEXTURE_MIN_FILTER, gl_context.NEAREST);
    gl_context.texParameteri(gl_context.TEXTURE_2D, gl_context.TEXTURE_MAG_FILTER, gl_context.NEAREST);
    gl_context.texParameteri(gl_context.TEXTURE_2D, gl_context.TEXTURE_WRAP_S, gl_context.CLAMP_TO_EDGE);
    gl_context.texParameteri(gl_context.TEXTURE_2D, gl_context.TEXTURE_WRAP_T, gl_context.CLAMP_TO_EDGE);
    gl_context.texImage2D(gl_context.TEXTURE_2D,0,gl_context.RGBA,
            office_width,office_height,0,gl_context.RGBA,gl_context.UNSIGNED_BYTE,null);
    framebuffer.texture = frame_texture;
        
    //创建深度缓冲区,保存深度值
    depthbuffer = gl_context.createRenderbuffer();
    gl_context.bindRenderbuffer(gl_context.RENDERBUFFER,depthbuffer);
    gl_context.renderbufferStorage(gl_context.RENDERBUFFER,gl_context.DEPTH_COMPONENT16,office_width,office_height);
        
    gl_context.bindFramebuffer(gl_context.FRAMEBUFFER,framebuffer);
    gl_context.framebufferTexture2D(gl_context.FRAMEBUFFER,gl_context.COLOR_ATTACHMENT0,gl_context.TEXTURE_2D,frame_texture,0);
    gl_context.framebufferRenderbuffer(gl_context.FRAMEBUFFER,gl_context.DEPTH_ATTACHMENT,gl_context.RENDERBUFFER,depthbuffer);
        
    var e = gl_context.checkFramebufferStatus(gl_context.FRAMEBUFFER);
    if(e!=gl_context.FRAMEBUFFER_COMPLETE)
    {
        alert(e.toString());
        return false;
    }
        
    return true;
}
     
     
//初始化WebGL
function init_webgl()
{
  canvas = document.getElementById("webGL");
       
  if(!canvas){
    alert("获取<Canvas>标签失败!");
    return;
}
     
  //获取webGL统计图上下文
  gl_context = canvas.getContext('webgl',
                { antialias:false,
                  stencil:true});
     
  if(!gl_context){
    alert("获取WebGL上下文失败!");
    return;
  }
     
  canvas.width = $("#area").width();
  canvas.height = $("#area").width()*3/5;
       
  canvas.onmousedown = function(ev){ click(ev);}
     
  //设置视口大小
  gl_context.viewport(0,0,canvas.width,canvas.height);
     
  //设置matView和matProject矩阵
  FlyMath.Matrix.LookAtRH(matView,camera,camera_to,camera_up);
  FlyMath.Matrix.PerspectiveRH(matProject,FlyMath_PI/3,1,1,100);
        
  draw();
}
    
//编译vs和fs,并创建program对象
function init_program(vs_src,fs_src)
{
    var vs = gl_context.createShader(gl_context.VERTEX_SHADER);
    gl_context.shaderSource(vs,vs_src);
    gl_context.compileShader(vs);
       
    if(!gl_context.getShaderParameter(vs,gl_context.COMPILE_STATUS)){
        alert(gl_context.getShaderInfoLog(vs));
        return;
    }
     
    var fs = gl_context.createShader(gl_context.FRAGMENT_SHADER);
    gl_context.shaderSource(fs,fs_src);
    gl_context.compileShader(fs);
       
    if(!gl_context.getShaderParameter(vs,gl_context.COMPILE_STATUS)){
        alert(gl_context.getShaderInfoLog(fs));
        return;
    }
     
//加载选择的顶点和片断
    var shaderProgram = gl_context.createProgram();
    gl_context.attachShader(shaderProgram, vs);
    gl_context.attachShader(shaderProgram, fs);
    gl_context.linkProgram(shaderProgram);
     
    if (!gl_context.getProgramParameter(shaderProgram, gl_context.LINK_STATUS)) {
        alert("Could not initialise shaders");
        return;
    } 
    return shaderProgram;
}
    
function draw()
{         
    
//===================绘制三角形到缓冲区
    var shaderProgram1  = init_program(vs_tri_src,fs_tri_src);
        //绑定shader中的参数变量
    shader_pos = gl_context.getAttribLocation(shaderProgram1, "a_Position");
    ColorUniform = gl_context.getUniformLocation(shaderProgram1,"a_color");
    shaderModelViewMatrixUniform = gl_context.getUniformLocation(shaderProgram1,"modelViewMatrix");
    shaderProjectionMatrixUniform = gl_context.getUniformLocation(shaderProgram1,"projectionMatrix");
        
    if(false==initFrameBuffer())
    {
        alert("error initFrameBuffer");
        return;
    }
        
    gl_context.useProgram(shaderProgram1);
        
    gl_context.bindFramebuffer(gl_context.FRAMEBUFFER,framebuffer);
    gl_context.viewport(0,0,office_width,office_height);
    gl_context.clearColor(0,0,0.3,1);
  //清空webgl颜色缓冲区里的内容
    gl_context.clear(gl_context.COLOR_BUFFER_BIT | gl_context.DEPTH_BUFFER_BIT);
    gl_context.enable(gl_context.DEPTH_TEST); 
            
    draw_triangle();
    
//=====================绘制正方形
        
    //初始化quad用的program对象
    var shaderProgram2  = init_program(vs_quad_src,fs_quad_src);
        
    //绑定shader中的参数变量
    shader_pos = gl_context.getAttribLocation(shaderProgram2, "a_Position");
    shader_uv = gl_context.getAttribLocation(shaderProgram2, "a_UV");
         
    shaderModelViewMatrixUniform = gl_context.getUniformLocation(shaderProgram2,"modelViewMatrix");
    shaderProjectionMatrixUniform = gl_context.getUniformLocation(shaderProgram2,"projectionMatrix");
    SamplerUniform = gl_context.getUniformLocation(shaderProgram2,"A_Texture");
        
    gl_context.useProgram(shaderProgram2);
        
    gl_context.bindFramebuffer(gl_context.FRAMEBUFFER,null);
    gl_context.viewport(0,0,canvas.width,canvas.height);
    //清空canvas的背景颜色
    gl_context.clearColor(0,0,0.5,1);
     
  //清空webgl颜色缓冲区里的内容
    gl_context.clear(gl_context.COLOR_BUFFER_BIT | gl_context.DEPTH_BUFFER_BIT);
    gl_context.enable(gl_context.DEPTH_TEST);
        
    draw_quad();  
}
    
//绘制三角形
function draw_triangle()
{
  var vertices = new Float32Array([0.5,0.5,-1,0.3,0.0,-1,0.7,0.0,-1]);
  var tri_buf = gl_context.createBuffer();
      
  gl_context.bindBuffer(gl_context.ARRAY_BUFFER, tri_buf);
  gl_context.bufferData(gl_context.ARRAY_BUFFER, new Float32Array(vertices), gl_context.STATIC_DRAW);
         
//激活缓冲区,渲染
  gl_context.enableVertexAttribArray(shader_pos);
  gl_context.vertexAttribPointer(shader_pos, 3, gl_context.FLOAT, false, 0, 0);
      
//绑定矩阵
  gl_context.uniformMatrix4fv(shaderModelViewMatrixUniform,false,matView);
  gl_context.uniformMatrix4fv(shaderProjectionMatrixUniform,false,matProject);
      
//绑定顶点顔色
  gl_context.uniform3f(ColorUniform,tri_color[0],tri_color[1],tri_color[2]);
      
  gl_context.drawArrays(gl_context.TRIANGLES,0,3);
    
}
     
//绘制正方形
function draw_quad()
{
/* 正方形顶点位置
    0 , 1
    2 , 3 
*/
    var vertices = new Float32Array([-1.0,1.0,-3,1.0,1.0,-3,-1.0,-1.0,-3,1.0,-1.0,-3]);
    var indices =  [0,1,2,1,2,3];
    var uvs = [0,1,1,1,0,0,1,0];
     
    var quad_buf = gl_context.createBuffer();
    var indexBuffer = gl_context.createBuffer();
    var uvBuffer = gl_context.createBuffer();
         
        
//激活缓冲区,渲染
    gl_context.enableVertexAttribArray(shader_pos);
    gl_context.enableVertexAttribArray(shader_uv);
         
    gl_context.bindBuffer(gl_context.ARRAY_BUFFER, quad_buf);
    gl_context.bufferData(gl_context.ARRAY_BUFFER, new Float32Array(vertices), gl_context.STATIC_DRAW);
    gl_context.vertexAttribPointer(shader_pos, 3, gl_context.FLOAT, false, 0, 0);
     
    gl_context.bindBuffer(gl_context.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl_context.bufferData(gl_context.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices), gl_context.STATIC_DRAW);
        
    gl_context.bindBuffer(gl_context.ARRAY_BUFFER,uvBuffer);
    gl_context.bufferData(gl_context.ARRAY_BUFFER,new Float32Array(uvs),gl_context.STATIC_DRAW);
    gl_context.vertexAttribPointer(shader_uv, 2, gl_context.FLOAT, false, 0, 0);
     
 //绑定矩阵
    gl_context.uniformMatrix4fv(shaderModelViewMatrixUniform,false,matView);
    gl_context.uniformMatrix4fv(shaderProjectionMatrixUniform,false,matProject);
      
//绑定纹理图片,设置纹理坐标方式
    gl_context.texParameteri(gl_context.TEXTURE_2D, gl_context.TEXTURE_MIN_FILTER, gl_context.NEAREST);
    gl_context.texParameteri(gl_context.TEXTURE_2D, gl_context.TEXTURE_MAG_FILTER, gl_context.NEAREST);
    gl_context.texParameteri(gl_context.TEXTURE_2D, gl_context.TEXTURE_WRAP_S, gl_context.CLAMP_TO_EDGE);
    gl_context.texParameteri(gl_context.TEXTURE_2D, gl_context.TEXTURE_WRAP_T, gl_context.CLAMP_TO_EDGE);
         
    gl_context.activeTexture(gl_context.TEXTURE0);
    gl_context.bindTexture(gl_context.TEXTURE_2D, framebuffer.texture);
         
    gl_context.uniform1i(SamplerUniform, 0);
            
    gl_context.drawElements(gl_context.TRIANGLES,indices.length,gl_context.UNSIGNED_BYTE, 0);
}

运行示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sun zi chao

你的鼓励是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值