第七章:着色器

高效GPU渲染方案

本章介绍着色器的基本知识以及Geiv下对其提供的支持接口。并以“渐变高斯模糊”为线索进行实例的演示解说。

[背景信息]

[计算机中央处理器的局限性]

在大学的“数字图像处理”课程中,老师解说了高斯模糊的基本算法。并使用C#进行了基本实现。高斯模糊。简单地说,就是使用高斯权重模板对图像的每个像素进行再计算、填充,以达到模糊的效果。

在课程中。对于给定的模板与模糊度系数,对一副800X600的图像进行模糊处理。须要计算48万个像素点,虽然当时机房已经普及了酷睿系列CPU。但这个过程依然耗时2~3秒。

于是有这种一个问题:在游戏中,我们常常看到画面进行模糊到清晰的动态转变。即使是次世代的GBA\SFC等游戏主机依旧有这种效果实现。而论硬件性能,我们的PC应该远远高于这些次世代游戏机,而2、3秒的运算结果已经明白告诉我,想要进行更为高效的模糊计算,显示地挨个计算像素点是不可能达到预期效率的,想要一秒钟完毕60幅不同模糊度的图像计算,还要另寻他路。

为什么计算性能普遍较低的游戏机却能实现高性能CPU都无法流畅做到的运算呢?笔者觉得还是由于CPU的结构差异导致的,PC的任务种类多。过程复杂。须要复杂的指令集系统。但游戏机的CPU主要为图像而生。仅仅需使用针对图像的少数精简指令系统就可以。

总而言之,为了保证CPU在复杂任务下的通用性,计算机的CPU并不擅长进行图形运算。而且在一些场合下,图形运算的效率相当低下。

[图形处理器GPU]

为了弥补CPU处理图形的缺陷,GPU便诞生了。GPU是针对图形运算而生的处理器,现在的主流游戏PC都会有一个高性能的GPU。详细的概念笔者就不在这里提了,相信搞计算机的都会对其了解一二。

[在编程中使用GPU运算资源]

在大多数开发场合下,不管是C还是Java。我们敲的代码都无疑地执行在CPU之上。那我们如何才干显示地调用GPU的资源呢?在OPENGL中,除了经常使用的固定管线式编程外,还提供了更为灵活的功能。它同意开发人员使用着色语言(GLSL,一种类C语言)编写名为“着色器”的程序,经编译执行于GPU之上并接管一部分GPU内置功能。

[Opengl着色器简述]

介于笔者履历有限,这里仅仅做简介,具体概念能够參考《Opengl着色语言》一书,它对着色器和GLSL有着很具体的介绍。

一个Opengl着色器(下简称着色器)由一个顶点着色器与片段着色器构成。它们负责的工作大部分是不同的,仅有少部分交集。

Opengl为着色器的校验、编译、连接和设置參数提供了完整的API,仅仅要显卡支持,在Opengl上下文中就可以由源文件得到着色器程序,无需其它环境。

编写着色器的目的。是为了实现诸如模糊、雾化、发光等固定管线不易叙述的功能,进而丰富图像的表现能力。非常多游戏引擎将Opengl中与着色器相关的API包装到了上层,我在设计时也採用了这样的方案。

[内置着色器]

在GEiv中,内置了5个系统着色器,它们是在设计之初进行着色器測试时所保留下来的。并非因为“经常使用”(笔者感觉着色器的编写往往非常有针对性,一般非常难有经常使用一说)。

依次介绍:

SD_ANTICOLOR:将目标色改为反色

SD_EMBOSS_MODE9:浮雕效果,后面的MODE9表示它使用的是3X3的模板,能够适应一般的需求,若要提高准确度,能够将模板扩充至4X4\5X5等。

SD_GAUSSIAN_MODE9:高斯模糊

SD_LAPLACIAN_MODE9:锐化

SD_MEAN_MODE9:均值化

[为图元设置着色器]

在GEiv中。使用着色器的对象是图元,使用其setShaderProgram方法将着色器绑顶到相应图元上。如:

Obj T =UES.creatObj(UESI.BGIndex);
T.addGLImage(0,0, "./mdls.jpg");
T.setShaderProgram(SysShader.SD_GAUSSIAN_MODE9);//将系统着色器绑定到图元T上

[设置Uniform參数]

Uniform修饰词(不是数据类型)指明一个GLSL中的变量是外部传入的。而且在整个着色器运行过程中不会改变(差别于attribute),比如对于实现模糊变化来讲,“模糊度”这个变量属于Uniform型无疑。

在GLSL中。数组有非常多存在形式,float[]能够表示一维线性浮点数组。vect2则表示了一个点对,而vec2[]则表示了一维线性浮点“数对”组,类似的还有vect3\4等等这样在java中没有现成的相应结构的数据。也就是说,设置Uniform參数时必须给定我们的数据源(一维线性数组)和映射到GLSL中变量的方式。

在GEiv的图元类中。setShaderUniform(String uniformName, Object value, intTPFLAG)方法能够设置该图元上绑定的着色器參数,这种方法的參数依次是:

uniformName是着色器中对于的变量名称。

value是数据源,眼下仅仅支持float[]、Float[]或单个的Float型。

TPFLAG 是指定的映射方式。你能够直接从ShaderController的静态值部分直接引用。

它能够是下列值之中的一个:

AFLOAT与FLOATS分别相应单个float值与一维float数组。

VERTXS表示由X个点对组成的数组,即该数组每一个元素包括一个X维度向量。所给数据源必须是一个X整数倍大小的一维线性数组,该数组会依照顺序依次填充这些X维度向量。比如使用TP_VERT2S时,数据源一维数组的前两个元素构成了着色器向量数组的第一个向量元素。

VERTEX表示一个X维度向量。要求给定的一维数据源数组大小必须为X。

[自己定义用户着色器]

除了使用SysShader中现成的着色器外,还能够使用用户自己定义的着色器程序。

在引擎的句柄UESI中,包装并简化的Opengl产生着色器的API:

UESI.createShaderProgram(String spName,String vpPath, String fpPath);

參数介绍:

spName是用户为着色器起的名字。

vpPath是顶点着色器的源文件路径,如”./vp.txt”。

fpPath是片段着色器源文件路径。

之后我们仅仅须要使用设置着色器的API对图元绑定spName即可了。与之前介绍的字库同样。spName也具有全局效应,能够为其他上下文的图元绑定。

[实例-渐变高斯模糊]

您能够在[GitHub]Sample\Sample-Shader下找到这个样例及使用的资源。

Main.java

package com.geiv.test;

import geivcore.R;
import geivcore.UESI; public class Main{
public static void main(String[] args) {
UESI UES = new R();
new Guassion(UES);
}
}

Guassion.java

package com.geiv.test;

import geivcore.SerialTask;
import geivcore.UESI;
import geivcore.engineSys.shadercontroller.ShaderController;
import geivcore.engineSys.shadercontroller.SysShader;
import geivcore.enginedata.canonical.CANExPos;
import geivcore.enginedata.obj.Obj; import java.awt.event.KeyEvent;
import java.util.Arrays; public class Guassion implements SerialTask{
UESI UES;
float[] g_aryVerticalOffset;
float[] vertstatic;
float bur = 600f;//bur作为模糊度因子,使用静态偏移量(vertstatic)除以模糊度因子得到不同的偏移量參数(g_aryVerticalOffset)。因为GLSL上下文是使用的OPENGL坐标式,与GEiv有所不同,因此初始因子经计算转换后定为600f Obj T;
public Guassion(UESI UES) {
this.UES = UES; T = UES.creatObj(UESI.BGIndex);
T.addGLImage(0, 0, "./mdls.jpg"); T.setShaderProgram(SysShader.SD_GAUSSIAN_MODE9);//设置着色器
T.setShaderUniform(SysShader.NA_WEIGHTARGS,SysShader.BR_GAUSSIAN_MODE9, ShaderController.TP_FLOATS);//设置Unfiorm參数,当中规格化后的权重模板及其在GLSL上下文中的名称已经给定。它们能够由SysShader直接引用。 vertstatic = new float[]{-1,-1, 0,-1, 1,-1
-1, 0, 0, 0, 1, 0
-1, 1, 0, 1, 1, 1};//偏移量模板。看不懂的话能够參考以下的图 g_aryVerticalOffset = Arrays.copyOf(vertstatic, vertstatic.length); for(int i = 0;i < g_aryVerticalOffset.length;i++)
{
g_aryVerticalOffset[i] = vertstatic[i]/bur;
} T.setShaderUniform(SysShader.NA_OFFSETARGS, g_aryVerticalOffset,ShaderController.TP_VERT2S);//设置Uniform偏移量
T.setPosition(CANExPos.POS_CENTER);
T.show(); UES.addSerialTask(this);
} @Override
public void Serial(int clock) {//挂载一个扫描式的键盘输入,当Z键按下时bur添加。重置偏移量并重设Uniform。 当X键按下时则相反。
if(UES.getKeyStatus(KeyEvent.VK_Z))
{
T.setShaderUniform(SysShader.NA_OFFSETARGS,g_aryVerticalOffset,ShaderController.TP_VERT2S);
bur+=4f;
for(int i = 0;i < g_aryVerticalOffset.length;i++)
{
g_aryVerticalOffset[i] = vertstatic[i]/bur;
}
}
else if(UES.getKeyStatus(KeyEvent.VK_X))
{
T.setShaderUniform(SysShader.NA_OFFSETARGS,g_aryVerticalOffset,ShaderController.TP_VERT2S);
bur-=4f;
for(int i = 0;i < g_aryVerticalOffset.length;i++)
{
g_aryVerticalOffset[i] = vertstatic[i]/bur;
}
}
}
}

关于偏移量模板的映射,实际上是这种↓:

除此之外,还有高斯模糊着色器的两部分源码

顶点着色器:

attribute float sys_pIndex;
void main(void)
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}

↑这个顶点着色器没有写不论什么实质性的功能,它被非常多片段着色器共用。

高斯-片段着色器:

const int g_iWeightNumber = 9;//权重模板大小
uniform sampler2D g_FilterTexture;//我们的纹理
uniform float g_aryWeight[g_iWeightNumber];//权重数组
uniform vec2 g_aryOffset[g_iWeightNumber];//偏移量-即使使用9个格子,也没有规定必须是相邻的格子;偏移量叙述着权重模板格子间的实际像素距离,取值越高,模糊度越高。 void main(void)
{
vec4 vec4Sum = vec4(0.0);
for(int i = 0; i < g_iWeightNumber; ++i)
{
vec4Sum += texture2D(g_FilterTexture, gl_TexCoord[0].st + g_aryOffset[i])*g_aryWeight[i];//这里进行实质的计算过程。也就是替换原先我们让CPU挨个计算像素点的部分。 gl_TexCoord[0]是绑定的0号纹理(Opengl中图形是能够绑定多个纹理的)。它的st就是位置,一个vec2类型量;位置与偏移量进行加和,得到偏移位置。之后依据纹理和偏移位置取出相应的rgba四维向量,它是一个vec4类型。进一步,将我们的vec4乘以规格化后的权重。并加和到vec4Sum上。因为是规格化后的权重,因此RGBA都不会越界。 }
gl_FragColor = vec4Sum;// 最后,我们将这个计算完成的RGBA结果填入纹理中。 }

最后,运行结果(按住X键时):

↑能够观察到由清晰到模糊的动态过程。

[总结]

本章介绍了着色器的使用场合以及GEiv下相应的API。

使用着色器是为了实现固定管线难以叙述的效果。

着色器执行于GPU,在对图像渲染时效率比直接使用CPU运算高出非常多。

版权声明:本文博客原创文章。博客,未经同意,不得转载。

[GEiv]第七章:着色器 高效GPU渲染方案的更多相关文章

  1. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十二章:几何着色器(The Geometry Shader)

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十二章:几何着色器(The Geometry Shader) 代码工 ...

  2. OpenGL管线(用经典管线代说着色器内部)

    图形管线(graphics pipeline)向来以复杂为特点,这归结为图形任务的复杂性和挑战性.OpenGL作为图形硬件标准,是最通用的图形管线版本.本文用自顶向下的思路来简单总结OpenGL图形管 ...

  3. OpenGL入门1.3:着色器 GLSL

    前言 经过之前一段时间的学习(渲染管线简介)我们已经知道了着色器(Shader)是运行在GPU上的程序,这些小程序为图形渲染管线的某个特定部分而运行,着色器只是一种把输入转化为输出的程序,着色器也是一 ...

  4. DirectX11--深入理解Effects11、使用着色器反射机制(Shader Reflection)实现一个复杂Effects框架

    前言 如果之前你是跟随本教程系列学习的话,应该能够初步了解Effects11(现FX11)的实现机制,并且可以编写一个简易的特效管理框架,但是随着特效种类的增多,要管理的着色器.资源等也随之变多.如果 ...

  5. OpenGL ES学习笔记(一)——基本用法、绘制流程与着色器编译

    首先声明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. 在Android.iOS等移动平台上 ...

  6. Direct3D 11 Tutorial 3: Shaders and Effect System_Direct3D 11 教程3:着色器和效果系统

    概述 在上一个教程中,我们设置了一个顶点缓冲区并将一个三角形传递给GPU. 现在,我们将逐步完成图形管道并查看每个阶段的工作原理. 将解释着色器和效果系统的概念. 请注意,本教程与前一个源代码共享相同 ...

  7. Unity3D学习笔记(三十四):Shader着色器(1)

    一.GPU:图形处理器,Graphics Processing Unit 显卡的处理器就是图形处理器.与CPU类似.   GPU和CPU的区别? 1.CPU主要是为了串行指令设计,GPU则是为了大规模 ...

  8. 在pixi中使用你的自定义着色器

    通过几天的学习,对openGL.shader有了一个大致的了解. 回到学习的初衷吧,在基于pixi.js重构D3项目的时候,因为精灵层级的问题,我得按照一定的先后顺序将不同类别的精灵添加到场景中去. ...

  9. (Python OpenGL)【3】着色器 PyOpenGL

    (Python OpenGL)现在开始我们使用着色器来进行渲染.着色器是目前做3D图形最流行的方式. OpenGL的渲染管线流程: 数据传输到OpenGL—>顶点处理器—>细分着色—> ...

随机推荐

  1. [Node.js] Use nodejs-dashboard event loop delay with hrtime()

    In this lesson, you will learn how to use the Formidable nodejs-dashboard event loop delay to identi ...

  2. git 分支建立及合并

    分支的新建与合并 让我们来看一个简单的分支新建与分支合并的例子,实际工作中你可能会用到类似的工作流. 你将经历如下步骤: 开发某个网站. 为实现某个新的需求,创建一个分支. 在这个分支上开展工作. 正 ...

  3. Android菜鸟的成长笔记(27)——SurfaceView的使用

    前面有关自定义View中进行了绘图,但View的绘图机制存在如下缺陷: 1.View缺乏双缓冲机制. 2.当程序需要更新View上的图像时,程序必须重绘View上显示的整张图片. 3.新线程无法直接更 ...

  4. CentOS7下安装Mysql失败经历--CentOS7使用yum安装和卸载Mysql过程

    起因 自己租用的BandwagonVPS上安装了个CentOS7,然后开始安装各种软件,结果yum安装MySQL发现MySQL在yum源中的Mysql不对劲,于是自己百度搜索安装方法. 终于我搜到了这 ...

  5. 栈溢出笔记1.9 认识SEH

    从本节開始,我们就要研究一些略微高级点的话题了,如同在1.2节中看到的,Windows中为抵抗栈溢出做了非常多保护性的检查工作,编译的程序默认开启了这些保护. 假设我们不能绕过这些保护.那么我们的Sh ...

  6. 切换-5.7-传统复制切换成GTID复制

    1.基本环境:     Master Slave MySQL版本 MySQL-5.7.16-X86_64 MySQL-5.7.16-X86_64 IP 192.168.56.156 192.168.5 ...

  7. 【codeforces 755C】PolandBall and Forest

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  8. produces在@requestMapping中的使用方式和作用

    produces可能不算一个注解,因为什么呢,它是注解@requestMapping注解里面的属性项, 它的作用是指定返回值类型,不但可以设置返回值类型还可以设定返回值的字符编码: 还有一个属性与其对 ...

  9. [ACM] POJ 1094 Sorting It All Out (拓扑排序)

    Sorting It All Out Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 26801   Accepted: 92 ...

  10. Java获取URL对应的资源

    Java获取URL对应的资源   认识IP.认识URL是进行网络编程的第一步.java.net.URL提供了丰富的URL构建方式,并可以通过java.net.URL来获取资源.   一.认识URL   ...