您的位置:首页 > 运维架构

OpenGL - Tessellation Shader 【转】

2015-12-01 15:37 344 查看
http://blog.sina.com.cn/s/blog_8c7d49f20102v4qm.html

Patch is just an ordered list of vertices (在tessellation shader里面比较重要的概念就是这个patch,patch是一系列的顶点,OpenGL规定patch的vertex数量必须至少大于等于3)

The tessellation process doesn’t operate on OpenGL’s classic geometric primitives: points, lines, and triangles, but uses a new primitive called a patch (Tessellation shader就是针对patch来进行处理的而并非点,县,三角形)

Patch – are processed by all of active shading stage in the pipeline (patch可被所有的shader处理)

Two shading stages of Tessellation shading:
1. Tessellation Control Shader (optional)

In tessellation control shader, gl_PatchVerticesIn provides the number of elements in gl_in (gl_in 用于在tessellation control shader里面访问传进的来patch里面的顶点)

gl_out 用于控制patch里面的vertex从tessellation control shader输出后的属性

gl_InvocationID is used to access the specific vertex of a patch (gl_InvocationID 用于访问传入patch里的特定顶点)

Layout (vertices = *) out; (用于指定输出的patch里面有多少个顶点)

gl_TessLevelInner

Specify how the interior of the domain is subdivided and stored in
a two element array named gl_TessLevelInner(指定多边形内部如何细分)

gl_TessLevelOuter

Control how the perimeter of the domain is subdivided, and is
stored in an implicitly declared four-element array named
gl_TessLevelOuter(指定多边形边界上的边被如何细分)

gl_TessLevelInner & gl_TessLevelOuter
根据多边形内部区域的类型会有不同的分割法

Tessellation Evaluation Shader (optional)

The TES is executed on all
generated domain
locations.Positions each of the
vertices in the final mesh (TES是针对从tessellation control shader
里面通过细分生成的顶点来进行运算,通过gl_in和gl_VocationID来访问我们传入的patch的一系列顶点信息,结合gl_TessCoord访问我们生成的新顶点的纹理信息来计算新的坐标位置,从而实现细分多边形的效果)

layout (quads, equal_spacing, ccw) in;
(指定新生成的多边形类型等信息)

glPatchParameteri() -- 告诉程序我们定义多少个顶点为一个patch

barrier() – .....
If you have additional per-vertex attribute values, either for
input or output, these need to be declared as either in or out
arrays in your tessellation control shader (需要传入TCS额外的顶点信息,需要定义额外in
& out array)

glPatchParameterfv() -- can be used to set the inner and
outer-tessellation levels(可以用于指定inner和outer的数值,当然我们也可以在tessellation
control shader里面通过gl_TessLevelInner &
gl_TessLevelOuter直接指定)

三中不同类型的domain -- 会决定我们inner和outer的具体含义:

Quad Tessellation:

…..

Isoline Tessellation:

Use only two of the outer-tessellation levels to determine the
amount of subdivision

…..

Triangle Tessellation:

Triangular domains use barycentric coordinates to specify their
Tessellation coordinates

…..

最终渲染效果:

- Tessellation Shader" title="OpenGL - Tessellation Shader" height="291" width="452">

- Tessellation Shader" title="OpenGL - Tessellation Shader" height="318" width="448">

- Tessellation Shader" title="OpenGL - Tessellation Shader" height="291" width="455">

- Tessellation Shader" title="OpenGL - Tessellation Shader" height="298" width="444">

Main.cpp Source Code below:

#include

#include

#include

// TODO: 在此处引用程序需要的其他头文件

#include

#include "vgl.h"

#include "mat.h"

#include "LoadShaders.h"

#include "Shapes/Teapot.h"

using namespace std;

GLuint PM; // Projection
matrix

GLuint
MVM;
// Model view matrix

GLuint InnerL; // Inner
tessellation paramter

GLuint OuterL; // Outer
tessellation paramter

GLfloat Inner = 1.0;

GLfloat Outer = 1.0;

#define SQUARE_VERTEX_NUMBER 4

//----------------------------------------------------------------------------

void init( void )

{

//create
shader and link program first

ShaderInfo
shaders[] = {

{
GL_VERTEX_SHADER,
"square.vert" },

{
GL_TESS_CONTROL_SHADER,
"square.cont" },

{
GL_TESS_EVALUATION_SHADER, "square.eval" },

{
GL_FRAGMENT_SHADER,
"square.frag" },

{ GL_NONE,
NULL }

};

//Shader
program

GLuint
program;

//load
shaders

program =
LoadShaders(shaders);

//link
shader program and use it

glUseProgram(program);

//vertex
array

GLuint
VA;

glGenVertexArrays(1, &VA);

glBindVertexArray(VA);

//Vertex
buffer

GLuint
VB;

glGenBuffers(1, &VB);

glBindBuffer(GL_ARRAY_BUFFER, VB);

//define
square vertex data

const
GLfloat square_vertex_data[SQUARE_VERTEX_NUMBER][2] = {

{ 1.0, 1.0
},

{ -1.0,
1.0},

{ -1.0,
-1.0},

{ 1.0,
-1.0}

};

//vertex
buffer data

glBufferData(GL_ARRAY_BUFFER, sizeof(square_vertex_data),
square_vertex_data, GL_STATIC_DRAW);

//Obtain
vertex position in program

GLint
vPostionIndex = glGetAttribLocation(program, "vPosition");

//Enable
vertex attribute

glEnableVertexAttribArray( vPostionIndex );

//define an
array of generic vertex attribute data for shader
(让shader知道怎么去读取我们传入的vertex 信息)

glVertexAttribPointer(vPostionIndex, 2, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0));

//Obtain
uniform variable of program

PM =
glGetUniformLocation(program, "PJ");

MVM =
glGetUniformLocation(program, "MV");

InnerL =
glGetUniformLocation(program, "inner");

OuterL =
glGetUniformLocation(program, "outer");

//Set some
default value for uniform variable

glUniform1f(InnerL, Inner);

glUniform1f(OuterL, Outer);

mat4 modelview = Translate( 0.0, 0.0, -2.0 )
* RotateX(
-50.0 );

glUniformMatrix4fv( MVM, 1, GL_TRUE, modelview );

//Define how
many vertices composed one patch (定义多少个vertices定义一个patch)

glPatchParameteri(GL_PATCH_VERTICES, 4);

//Enable
some relative setting

glEnable(GL_DEPTH_TEST);

glClearColor( 0.0, 0.0, 0.0, 1.0 );

}

//----------------------------------------------------------------------------

void display( void )

{

glClear(
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

glUniform1f(InnerL, Inner);

glUniform1f(OuterL, Outer);

glDrawArrays(GL_PATCHES, 0, SQUARE_VERTEX_NUMBER);

glutSwapBuffers();

}

//----------------------------------------------------------------------------

void reshape( int width, int height )

{

glViewport(
0, 0, width, height );

GLfloat aspect = GLfloat(width)/height;

mat4 projection = Perspective( 60.0, aspect, 1, 3
);

glUniformMatrix4fv( PM, 1, GL_TRUE, projection );

glutPostRedisplay();

}

//----------------------------------------------------------------------------

void keyboard( unsigned char key, int x, int y )

{

switch ( key
) {

case 'q':
case 'Q': case 033 :

exit(
EXIT_SUCCESS );

break;

case
'i':

Inner--;

if ( Inner
< 1.0 ) Inner = 1.0;

glUniform1f(
InnerL, Inner );

break;

case
'I':

Inner++;

if ( Inner
> 64 ) Inner = 64.0;

glUniform1f(
InnerL, Inner );

break;

case
'o':

Outer--;

if ( Outer
< 1.0 ) Outer = 1.0;

glUniform1f(
OuterL, Outer );

break;

case
'O':

Outer++;

if ( Outer
> 64 ) Outer = 64.0;

glUniform1f(
OuterL, Outer );

break;

case
'r':

Inner =
1.0;

Outer =
1.0;

glUniform1f(
InnerL, Inner );

glUniform1f(
OuterL, Outer );

break;

case 'm':
{

static
GLenum mode = GL_FILL;

mode = (
mode == GL_FILL ? GL_LINE : GL_FILL );

glPolygonMode( GL_FRONT_AND_BACK, mode );

} break;

}

glutPostRedisplay();

}

//----------------------------------------------------------------------------

int main( int argc, char *argv[] )

{

glutInit(
&argc, argv );

glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );

glutInitWindowSize( 512, 512 );

glutInitContextVersion( 3, 2 );

glutInitContextProfile( GLUT_CORE_PROFILE );

glutCreateWindow( "teapot" );

glewExperimental =
GL_TRUE;
//注意这里有个坑 --
这一句很关键,不加这一句gl_GenVertexArray()会报错,好像说是GLEW对openGL的core
context有一些问题

//
http://stackoverflow.com/questions/22813625/0xc0000005-access-violation-executing-location-0x00000000-opengl
GLenum error
= glewInit();

if( error !=
GLEW_OK )

{

cout<<"glewInit failed, aborting"<<endl;

}

if (
GLEW_ARB_vertex_array_object == NULL )

{

cout<<"GLEW_ARB_vertex_array_object =
NULL"<<endl;

}

cout<<"Error info:"<<glGetError()<<endl;

init();

glutDisplayFunc( display );

glutReshapeFunc( reshape );

glutKeyboardFunc( keyboard );

glutMainLoop();

return
0;

}

square.vert source code below:

#version 400 core

in vec4 vPosition;

void main()

{

//gl_Position is used to access the vertex position that is input
from application

gl_Position
= vPosition;

}

square.cont source code below:

#version 400 core

//The main purpose of tessellation control shader is:

//Generate the tessellation output patch vertices that are passed
to the tessellation

//evaluation shader, as well as update any per-vertex

//Define how many vertices will be used as one patch

layout (vertices = 4) out;

//uniform type is used to define the variable that can be used to
communicate between shader and application

uniform float inner;

uniform float outer;

void main()

{

//gl_TessLevelInner is used to define:

//how the
interior of the domain is subdivided and stored in a two element
array named

gl_TessLevelInner[0] = inner;

gl_TessLevelInner[1] = inner;

//gl_TessLevelOuter is used to define:

//how the
perimeter of the domain is subdivided, and is stored in an

//implicitly
declared four-element array

gl_TessLevelOuter[0] = outer;

gl_TessLevelOuter[1] = outer;

gl_TessLevelOuter[2] = outer;

gl_TessLevelOuter[3] = outer;

//gl_in is
used to access the number of elements that are define by
glPatchParameteri()

//glPatchParameteri() define how many vertices as a patch

//gl_out is
used to access the output vertex position of tessellation control
shader

//gl_in
vertex shader structure below:

//in
gl_PerVertex {

//
vec4
gl_Position;

//
float
gl_PointSize;

//
float
gl_ClipDistance[]

//}
gl_in[gl_PatchVerticesIn];

//gl_out
vertex shader structure is similar to gl_in structure

//gl_InvocationID is used to access the specific vertex of a
patch

gl_out[gl_InvocationID].gl_Position =
gl_in[gl_InvocationID].gl_Position;

}

square.eval source code below:

#version 400 core

//The main purpose of tessellation evaluation shader is:

//configure the primitive generator, which is done using a layout
directive

//Specifying the face winding for generated primitives

//(the order the vertices are issued determines the facedness of
the primitive)

layout (quads, equal_spacing, ccw) in;

//The TES is executed on all generated domain locations.

//The bound tessellation evaluation shader is

//executed one for each tessellation coordinate that the primitive
generator

//Tessellation Shaders emits, and is responsible for determining
the position

//of the vertex derived from the tessellation coordinate.

uniform mat4 PJ;

uniform mat4 MV;

#define M_PI

3.14159265358979323846

//----------------------------------------------------------------------------

float Hanning( vec2 p )

{

p -= 0.5; //
map unit square to [-.5, .5]

float r =
length( p );

r = cos(
M_PI * r / 2.0 );

r *=
r;

return
r;

}

void main()

{

//gl_TessCoord is used to access the Tessellation coordinates that
are

//generated
by tessellation control shader

float u =
gl_TessCoord.x;

float v =
gl_TessCoord.y;

//use
Tessellation coordinates to calculate position for new vertex
that

//is
generated by tessellation control shader

#define
p(i) gl_in[i].gl_Position

vec4 pos =
v*(u*p(0) + (1-u)*p(1)) + (1-v)*(u*p(3) + (1-u)*p(2));

pos.z =
Hanning( gl_TessCoord.xy );

gl_Position
= PJ * MV * pos;

}

square.frag source code below:

#version 400 core

out vec4 fColor;

void main()

{

fColor = (1
- gl_FragCoord.z) * vec4( 1.0, 0.0, 0.0, 1.0 );

}

总结:

tessellation shader是可选的shader,不是必须的

tessellation shader与vertex shader不一样,tessellation
shader是针对patch(一系列顶点)来处理而不是一个顶点 (因为tessellation
shader需要通过传入的patch(一系列顶点)来计算新顶点的位置信息)

tessellation control shader负责对patch的细分设定

tessellation evaluation
shader负责对TCS细分出来的顶点进行位置等信息运算从而实现LOD(level of detail --
根据与camera的距离不同而细分程度不同)等效果

Bezier曲线在这里是一种细分后位置的计算方法来实现曲面的平滑效果

tessellation shader有个重要的应用就是LOD(Level of
Detail),通过在判断物体与视线的距离来设定tessellation control里面的factor
level从而实现近细分多,远细分少的效果

还有一个应用叫displacement mapping,在tessellation evaluation
shader里面通过tessellation coordinate的值来映射纹理(sample a texture)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: