《OpenGL ES 3.x游戏开发(下卷)》一1.7 多重渲染目标

1.7 多重渲染目标

上一节已经介绍了帧缓冲与渲染缓冲的相关知识与案例,相信读者对于自定义帧缓冲与渲染缓冲的使用已有所了解。本节将介绍多重渲染目标(Multiple Render Targets),主要分为基本原理和简单案例两部分进行介绍。

1.7.1 基本知识

多重渲染目标允许程序同时渲染到多个颜色缓冲,向不同的颜色缓冲中送入渲染结果的不同方面(如不同RGBA色彩通道的值、深度值等)。不少高级特效渲染时需要使用多重渲染目标技术,例如延迟着色、屏幕空间环境光遮蔽等。下面介绍使用多重渲染目标技术的基本步骤。

(1)首先需要创建一个自定义的帧缓冲,并绑定到此帧缓冲。

(2)接着可以创建并初始化一批纹理,总数量等于要输出的不同渲染结果方面的数量,并且不能超过系统的最大限制数。

(3)然后将这一批纹理一一连接到自定义帧缓冲中的不同颜色附件中。

(4)接着在绘制时正常绘制前调用glDrawBuffers方法设置要输出的颜色附件。

(5)最后在片元着色器中定义多个输出变量一一对应到要输出的颜色附件。

提示

看完上述步骤之后,读者可能不是很明白。没有关系,后面会给出专门的案例供读者学习,那时就很清楚了。

了解了上述步骤后还有两个相关的方法需要介绍一下,具体内容如下。

(1)首先给出的是设置绘制时要输出颜色附件的glDrawBuffers方法,其方法签名如下。

1 public static void glDrawBuffers (int n, int[] bufs, int offset)

说明

参数n为要输出的颜色附件的数量,参数bufs为存放了n个要输出的颜色附件标志值的数组,参数offset为bufs数组的偏移量。

(2)接着介绍用于查询系统所支持最大输出颜色附件数的glGetIntegerv方法,其使用时的代码片段如下。

1    int[] params=new int[1];                    //声明数组用于记录颜色附件的最大值
2    GLES30.glGetIntegerv(GLES30.GL_MAX_COLOR_ATTACHMENTS, params, 0);//查询颜色附件最大值

说明

从上述代码片段中可以看出,首先需要声明一个数组用于存放查询的结果值,然后以参数GL_MAX_COLOR_ATTACHMENTS调用glGetIntegerv方法进行查询即可。另外,所有的OpenGL 3.0实现都支持的颜色附件最小值是4,若读者需要使用的量小于等于4时可以放心直接使用。

1.7.2 一个简单的案例

上一小节介绍了关于多重渲染目标的基本知识,本小节将给出一个使用多重渲染目标的简单案例Sample1_6。首先给出的是该案例的运行效果图,如图1-7和图1-8所示。

说明

图1-7和图1-8为本小节案例Sample1_6的运行效果图,其中图1-7为案例开始运行时的效果图,图1-8为手指向下滑动屏幕时的效果图。由于本书采用灰度印刷,对于此案例而言读者很难看到效果,这里建议读者使用真机运行本案例进行观察。

案例Sample1_6同时也使用了二次渲染技术,其在第一轮渲染时使用了多重渲染目标。将RGBA四个色彩通道的值送到自定义帧缓冲的0号颜色附件,将R、G、B三个色彩通道的值各自单独送到了1、2、3号颜色附件。每个颜色附件本身是一幅纹理,然后在第二轮绘制时将4个颜色附件对应的纹理分别渲染到了一个纹理矩形上,此时就看到了如图1-7和图1-8所示的情况。

本小节案例是基于前面的案例Sample1_5改变而来的,各个类的基本框架大致相同,因此相同的部分不再重复赘述,在此只介绍几处有代表性的部分,具体内容如下。

(1)首先介绍SceneRenderer类中用于初始化帧缓冲的initFBO方法,该方法的主要功能为声明指定长度的颜色附件常量数组、初始化帧缓冲、初始化渲染缓冲、绑定颜色附件以及设置纹理的采样方式和拉伸方式等,其具体代码如下。

1    public boolean initFBO(){
2         int[] attachments=new int[]{GLES30.GL_COLOR_ATTACHMENT0,//声明颜色附件常量数组
3               GLES30.GL_COLOR_ATTACHMENT1,GLES30.GL_COLOR_ATTACHMENT2,
4               GLES30.GL_COLOR_ATTACHMENT3};   //包含颜色附件0~3的标志值
5         int tia[]=new int[1];                 //用于存放创建帧缓冲编号的数组
6         GLES30.glGenFramebuffers(1, tia, 0);  //创建一个帧缓冲
7         frameBufferId=tia[0];                 //将帧缓冲编号记录到成员变量中
8         GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBufferId); //绑定帧缓冲
9         GLES30.glGenRenderbuffers(1, tia, 0); //创建一个渲染缓冲用作深度缓冲
10        renderDepthBufferId=tia[0];           //将渲染缓冲编号记录到成员变量中
11        //绑定用作深度缓冲的渲染缓冲
12        GLES30.glBindRenderbuffer(GLES30.GL_RENDERBUFFER, renderDepthBufferId);
13        GLES30.glRenderbufferStorage(GLES30.GL_RENDERBUFFER,  //为渲染缓冲初始化存储
14            GLES30.GL_DEPTH_COMPONENT16,GEN_TEX_WIDTH, GEN_TEX_HEIGHT);
15        GLES30.glFramebufferRenderbuffer(      //设置自定义帧缓冲的深度缓冲附件
16            GLES30.GL_FRAMEBUFFER,             //帧缓冲类型
17            GLES30.GL_DEPTH_ATTACHMENT,        //附件类型——深度附件
18            GLES30.GL_RENDERBUFFER,            //附件为渲染缓冲
19            renderDepthBufferId                //深度渲染缓冲编号
20         );
21        GLES30.glGenTextures(textureIds.length,textureIds,0);    //创建4个纹理
22        for(int i=0;i<attachments.length;i++){//遍历颜色附件常量数组
23             GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,textureIds[i]);  //绑定纹理
24             GLES30.glTexImage2D(             //设置颜色附件纹理的格式
25                 GLES30.GL_TEXTURE_2D,        //纹理类型
26                 0,                           //层次
27                 GLES30.GL_RGBA,              //内部格式
28                 GEN_TEX_WIDTH,               //宽度
29                 GEN_TEX_HEIGHT,              //高度
30                 0,                           //边界宽度
31                 GLES30.GL_RGBA,              //格式
32                 GLES30.GL_UNSIGNED_BYTE,                        //每个像素数据的格式
33                 null);
34             GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,        //设置MIN采样方式
35                 GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_NEAREST);
36             GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,        //设置MAG采样方式
37                 GLES30.GL_TEXTURE_MAG_FILTER,GLES30.GL_LINEAR);
38             GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,        //设置S轴拉伸方式
39                 GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_CLAMP_TO_EDGE);
40             GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,        //设置T轴拉伸方式
41                 GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_CLAMP_TO_EDGE);
42             GLES30.glFramebufferTexture2D(            //设置自定义帧缓冲的颜色附件
43                  GLES30.GL_DRAW_FRAMEBUFFER,          //帧缓冲类型
44                  attachments[i],                      //附件类型——颜色附件i
45                  GLES30.GL_TEXTURE_2D,                //附件为2D纹理
46                   textureIds[i],                      //纹理id
47                   0);                                 //纹理层次
48        }
49        GLES30.glDrawBuffers(attachments.length, attachments,0);//设置要输出的颜色附件
50        if(GLES30.GL_FRAMEBUFFER_COMPLETE !=                 //检查帧缓冲的完整性
51            GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER)){return false;}
52        return true;
53    }
  • 第2~第4行声明了长度为4的颜色附件标志值常量数组,在将指定纹理连接到帧缓冲中的特定颜色附件时使用。
  • 第5~第8行创建并绑定到了一个自定义的帧缓冲。
  • 第9~第20行为初始化渲染缓冲的相关代码,其中首先创建了一个渲染缓冲,然后绑定到了此渲染缓冲,并为其初始化了存储,接着将其作为前面自定义帧缓冲的深度附件。
  • 第21~第48行为对多重渲染目标所需的4个颜色附件进行初始化的相关代码,其中首先创建了4个纹理,然后对每个纹理进行初始化,并把这4个纹理分别连接到了0~3号颜色附件。
  • 第49~第52行首先调用glDrawBuffers方法设置要输出的颜色附件,并检查帧缓冲的完整性。若检查成功则返回true,否则返回false。
    (2)介绍完了初始化帧缓冲的initFBO方法后,接下来介绍用于绘制纹理矩形的drawShadowTexture方法,具体代码如下。
1    public void drawShadowTexture(){                        //绘制纹理矩形的方法
2         GLES30.glClearColor(0.5f,0.5f,0.5f,1.0f);          //设置屏幕背景色RGBA
3         GLES30.glViewport(0,0,SCREEN_WIDTH,SCREEN_HEIGHT); //设置视口大小及位置
4         GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);    //绑定到系统默认帧缓冲
5         //清除深度缓冲与颜色缓冲
6         GLES30.glClear(GLES30.GL_DEPTH_BUFFER_BIT |GLES30.GL_COLOR_BUFFER_BIT);
7         MatrixState.setProjectOrtho(-ratio, ratio, -1, 1, 2, 100);  //设置正交投影
8         MatrixState.setCamera(0,0,3,0f,0f,0f,0f,1.0f,0.0f);//调用此方法产生摄像机9参数矩阵
9         MatrixState.pushMatrix();                       //保护现场
10        MatrixState.translate(-ratio/2, 0.5f, 0);       //执行平移
11        tr.drawSelf(textureIds[0]);                     //绘制左上角纹理矩形
12        MatrixState.popMatrix();                        //恢复现场
13        MatrixState.pushMatrix();                       //保护现场
14        MatrixState.translate(ratio/2, 0.5f, 1);        //执行平移
15        tr.drawSelf(textureIds[1]);                     //绘制右上角纹理矩形
16        MatrixState.popMatrix();                        //恢复现场
17        MatrixState.pushMatrix();                       //保护现场
18        MatrixState.translate(-ratio/2, -0.5f, 0);      //执行平移
19        tr.drawSelf(textureIds[2]);                     //绘制左下角纹理矩形
20        MatrixState.popMatrix();                        //恢复现场
21        MatrixState.pushMatrix();                       //保护现场
22        MatrixState.translate(ratio/2, -0.5f, 1);       //执行平移
23        tr.drawSelf(textureIds[3]);                     //绘制右下角纹理矩形
24        MatrixState.popMatrix();                        //恢复现场
25    }
  • 第2~第6行的功能为设置屏幕背景颜色,设置视口大小与位置,绑定系统默认帧缓冲以及清除深度缓冲与颜色缓冲。
  • 第7~第8行设置了正交投影,并调用setCamera方法产生摄像机9参数矩阵。
  • 第9~第13行的功能为保护现场,平移坐标系并绘制左上角的小纹理矩形。
  • 第14~第24行的功能也是通过平移分别绘制右上角小纹理矩形、左下角小纹理矩形以及右下角小纹理矩形。
    (3)上面介绍了用于绘制4个小纹理矩形的drawShadowTexture方法,接下来介绍带有4个输出变量(对应于前面的4个颜色附件)的多重渲染目标片元着色器,其具体代码如下。
1    #version 300 es
2    precision mediump float;                 //给出默认的浮点精度
3    uniform sampler2D sTexture;              //纹理内容数据
4    in vec4 ambient;                         //接收从顶点着色器过来的环境光最终强度
5    in vec4 diffuse;                         //接收从顶点着色器过来的散射光最终强度
6    in vec4 specular;                        //接收从顶点着色器过来的镜面最终强度
7    in vec2 vTextureCoord;                   //接收从顶点着色器过来的纹理坐标
8    layout (location=0)out vec4 fragColor0;  //对应0号颜色附件的输出变量
9    layout (location=1)out vec4 fragColor1;  //对应1号颜色附件的输出变量
10    layout (location=2)out vec4 fragColor2; //对应2号颜色附件的输出变量
11    layout (location=3)out vec4 fragColor3; //对应3号颜色附件的输出变量
12    void main(){
13         vec4 finalColor=texture(sTexture, vTextureCoord);    //进行纹理采样
14         vec4 fragColor = finalColor*ambient+finalColor*specular+finalColor*diffuse;
           //记录最终颜色值
15         fragColor0=fragColor;    //将RGBA四个色彩通道综合值输出到0号颜色附件
16         fragColor1=vec4(fragColor.r,0.0,0.0,1.0);    //将R色彩通道值输出到1号颜色附件
17         fragColor2=vec4(0.0,fragColor.g,0.0,1.0);    //将G色彩通道值输出到2号颜色附件
18         fragColor3=vec4(0.0,0.0,fragColor.b,1.0);    //将B色彩通道值输出到3号颜色附件
19    }

说明

上述片元着色器的代码与前面案例Sample1_5中片元着色器的代码大致相同,不同的是该片元着色器中含有对应前面4个颜色附件的输出变量,从而实现了多重渲染目标的功能。

时间: 2024-09-24 20:12:18

《OpenGL ES 3.x游戏开发(下卷)》一1.7 多重渲染目标的相关文章

《OpenGL ES 3.x游戏开发(下卷)》一导读

前 言 OpenGL ES 3.x游戏开发(下卷) 为什么要写这样一套书 随着智能手机硬件性能的不断提升,如"水果忍者""极品飞车""狂野飙车8:极速凌云"等一批优秀的3D游戏娱乐应用在广大智能机用户间流行开来.与此同时,也带动了手机游戏产业逐渐从2D走向3D.但目前国内专门介绍3D游戏开发的书籍与资料都非常少,同时3D应用开发的门槛又比较高,使得很多初学者无从下手.根据这种情况,笔者结合多年从事3D游戏应用开发的经验编写了这样一套书. 了解一些

《OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例》——6.4节点法向量和面法向量

6.4 点法向量和面法向量 OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例 本章前面几节的案例都是基于球面开发的,球面属于连续.平滑的曲面,因此面上的每个顶点都有确定的法向量.但现实世界中的物体表面并不都是连续.平滑的,此时对于面上的某些点的法向量计算就不那么直观了,图6-18说明了这个问题. 从图6-18中可以看出,顶点A位于长方体左.上.前3个面的交界处,此处是不光滑的.这种情况下顶点A的法向量有两种处理策略,具体如下所列. 在顶点A的位置放置3个不同的顶点,每个顶点看作是仅

《OpenGL ES 3.x游戏开发(上卷)》一第1章 Android概述

第1章 Android概述 OpenGL ES 3.x游戏开发(上卷)随着移动互联网时代的到来,智能手机逐渐走进了人们的生活, Google公司于2007年11月5日发布了基于Linux平台的开源手机操作系统--Android.由于Android系统的开源性以及其他各个方面的因素,其受到了广大手机厂商的青睐,因此需要大量的Android开发人员来满足日益增长的海量软件开发需求. 提示 有些读者可能会有一点奇怪,不是介绍OpenGL ES 3.0的知识吗,怎么一开始就介绍Android呢?这是因为

《OpenGL ES 3.x游戏开发(上卷)》一第2章 游戏开发相关的 Android基础知识

第2章 游戏开发相关的 Android基础知识 OpenGL ES 3.x游戏开发(上卷)虽然本书主要是介绍OpenGL ES 3.0 3D应用及游戏开发的,但由于很多3D游戏应用中还需要用到目标平台的一些其他应用开发方面的知识,而本书3D基础知识部分主要是借助于Android平台来介绍OpenGL ES 3.0 3D应用及游戏开发的.故本章将向读者简要介绍一些在Android游戏开发中必备的一些基础知识,如音效.文件的读取.游戏信息的存储.Socket网络.蓝牙网络等. 提示 由于介绍Andr

OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例》一6.3 定位光与定向光

6.3 定位光与定向光 OpenGL ES 2.0游戏开发(上卷):基础技术和典型案例上一节中介绍的光照效果都是基于定位光光源的,定位光光源类似于现实生活中的白炽灯灯泡,其在某个固定的位置,发出的光向四周发散.定位光照射的一个明显特点就是,在给定光源位置的情况下,对不同位置的物体产生的光照效果不同. 现实世界中并不都是定位光,例如照射到地面上的阳光,光线之间是平行的,这种光称为定向光.定向光照射的明显特点是,在给定光线方向的情况下,场景中不同位置的物体反映出的光照效果完全一致.图6-16中对定位

《OpenGL ES 3.x游戏开发(下卷)》一1.8 小结

1.8 小结 本章主要介绍了顶点缓冲区对象.顶点数组对象.一致缓冲区对象.映射缓冲区对象.复制缓冲区对象.从颜色缓冲区复制纹理数据.帧缓冲与渲染缓冲以及多重渲染目标等关于缓冲的基本知识与技术,并给出了一些典型的小案例. 通过本章的学习,读者可以初步领会缓冲区对象所起的重要作用,为以后开发更加复杂.更高性能的3D应用或游戏奠定了良好的基础.

《OpenGL ES 3.x游戏开发(下卷)》一1.2 顶点数组对象

1.2 顶点数组对象 使用了顶点缓冲技术后,绘制效率有了较大的提升.但是还有一点不尽如人意,那就是顶点的位置坐标.法向量.纹理坐标等不同方面的数据每次使用时需要单独指定,重复了一些不必要的工作.OpenGL ES 3.0考虑到了这一点,提供了一种专门用于解决此问题的对象--顶点数组对象(VAO).本节将介绍顶点数组对象. 1.2.1 基本知识与案例效果 顶点数组对象的主要功能就是将绘制一个物体时所需的对应于不同方面(如顶点坐标.法向量.纹理坐标)的顶点缓冲及相关设置包装成一个整体,绘制时直接使用

《OpenGL ES 3.x游戏开发(下卷)》一2.7 固定渲染管线与可编程渲染管线实现方案的对比

2.7 固定渲染管线与可编程渲染管线实现方案的对比 本章最开始提到过,在固定渲染管线平台上想高效地实现本章案例中的特效是非常困难的.这是因为在固定渲染管线中,顶点数据一旦送入渲染管线后就不可能对其方便地自定义处理了.因此,在固定渲染管线上想实现本章案例中的特效只能采用以下两种策略之一. 提示 回顾一下,OpenGL ES 1.x(含1.0和1.1)采用的是固定渲染管线,从OpenGL ES 2.0开始采用可编程渲染管线. 1.初始化时预先计算数据此种策略的基本思想非常简单,就是在初始化时将动画中

《OpenGL ES 3.x游戏开发(下卷)》一1.1 顶点缓冲区

1.1 顶点缓冲区 将绘制物体的顶点数据保存在内存中,在调用glDrawArrays或者glDrawElements等绘制方法前需要调用相应的方法将数据送入显存,I/O开销大,性能不够好. 若采用顶点缓冲区对象存放顶点数据,则不需要在每次绘制前都将顶点数据复制进显存,而是在初始化顶点缓冲区对象时一次性将顶点数据送入显存,每次绘制时直接使用显存中的数据,可以大大提高渲染性能. 1.1.1 基本知识 OpenGL ES 3.0中支持两种类型的顶点缓冲区对象,分别为数组缓冲区对象(Array Buff