如何访问曲面细分控制着色器中同一面片中的所有顶点
How to access all vertexes within the same patch in Tessellation Control Shader
我想在 Tessenllation Control Shader 中做 LOD。而我的方法是计算每个patch在屏幕坐标上所占的面积,并为它们设置不同的tessellation level。
所以我需要访问补丁中的所有顶点,我这样做是这样的:
for(int i = 0; i < 4; i++)
{
position_screen[i] = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
}
我在 TCS 中定义了我的补丁,例如:
#version 400
layout( vertices=4 ) out;
这里是OpenGL中的相关代码:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rect_index_buffer);
glPatchParameteri(GL_PATCH_VERTICES, 4);
glDrawElements(GL_PATCHES, RECT_INDEX_SIZE, GL_UNSIGNED_INT, 0);
然而结果是strange.The曲面细分级别与屏幕上的区域相关,但所有补丁具有相同的细分级别。
所以问题是什么?
我想这是我尝试访问补丁中的顶点的方式出错了。那我该怎么做呢?
以下是我的Tessellation Control Shader中的代码,希望对你有所帮助:
#version 400
layout( vertices=4 ) out;
uniform mat4 ProjectionMatrix;
uniform mat4 ModelViewMatrix;
uniform float window_height;
uniform float window_width;
float PI = 3.14159;
float calcTriangleArea(float l[3]) //Heron's formula
{
float p = (l[0] + l[1] + l[2]) / 2.0;
return sqrt(p * (p - l[0]) * (p - l[1]) * (p - l[2]));
}
float calcSqureArea(vec4 position[4])
{
vec2 position_screen[4];
for(int i=0;i<4;i++)
{
position_screen[i] = position[i].xy;
}
float l[4];
for(int i = 0;i < 4;i++)
{
l[i] = length(position_screen[(i + 1) % 4] - position_screen[i % 4]);
}
float diagonal = length(position_screen[2] - position_screen[0]);
float l1[3];
float l2[3];
l1[0] = l[0];
l1[1] = l[1];
l1[2] = diagonal;
l2[0] = l[2];
l2[1] = l[3];
l2[2] = diagonal;
float area = calcTriangleArea(l1) + calcTriangleArea(l2);
return area;
}
float checkInsideView(vec4 position[4]) //check if the patch is visible
{
int flag = 4;
for(int i=0;i<4;i++)
{
if((position[i].x >= -window_width / 2.0) && (position[i].x <= window_width / 2.0) &&
(position[i].y >= -window_height / 2.0) && (position[i].y <= window_height / 2.0))
{
flag --;
}
}
if(flag == 0) //all 4 vertices are visible
{
return 0.0;
}
else if(flag == 4) //not all visible
{
return 2.0;
}
else //all vertices are not visible
{
return 1.0;
}
}
float calcLODLevel()
{
vec4 position_screen[4];
for(int i = 0; i < 4; i++)
{
position_screen[i] = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
}
float in_view_level = checkInsideView(position_screen);
//tess number is decided by the area that this patch covers on
//the screen
float area = calcSqureArea(position_screen);
float level = sqrt(area);
if(in_view_level == 1.0)
{
level /= sqrt(2);
}
//dont do tessellation
//if this patch is not visible
else if(in_view_level == 2.0)
{
level = 1.0;
}
return level;
}
void main()
{
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
float lod_level = calcLODLevel();
gl_TessLevelOuter[0] = lod_level;
gl_TessLevelOuter[1] = lod_level;
gl_TessLevelOuter[2] = lod_level;
gl_TessLevelOuter[3] = lod_level;
gl_TessLevelInner[0] = lod_level;
gl_TessLevelInner[1] = lod_level;
}
我认为问题出在你计算屏幕坐标,导致镶嵌层次太小。关键部分是这样的:
position_screen[i] = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
您在这里计算的是剪辑坐标,而不是屏幕坐标。要从剪辑坐标获取屏幕坐标,您需要:
- 进行透视分割。这会为您提供 [-1.0, 1.0].
范围内的 NDC(归一化设备坐标)
- 从 NDC 计算屏幕坐标。
在代码中,计算可能如下所示:
vec4 posClip = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
vec2 posNdc = posClip.xy * (1.0 / posClip.w);
vec2 posScreen = 0.5 * (posNdc + 1.0) * vec2(window_width, window_height);
我想在 Tessenllation Control Shader 中做 LOD。而我的方法是计算每个patch在屏幕坐标上所占的面积,并为它们设置不同的tessellation level。
所以我需要访问补丁中的所有顶点,我这样做是这样的:
for(int i = 0; i < 4; i++)
{
position_screen[i] = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
}
我在 TCS 中定义了我的补丁,例如:
#version 400
layout( vertices=4 ) out;
这里是OpenGL中的相关代码:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rect_index_buffer);
glPatchParameteri(GL_PATCH_VERTICES, 4);
glDrawElements(GL_PATCHES, RECT_INDEX_SIZE, GL_UNSIGNED_INT, 0);
然而结果是strange.The曲面细分级别与屏幕上的区域相关,但所有补丁具有相同的细分级别。
所以问题是什么?
我想这是我尝试访问补丁中的顶点的方式出错了。那我该怎么做呢?
以下是我的Tessellation Control Shader中的代码,希望对你有所帮助:
#version 400
layout( vertices=4 ) out;
uniform mat4 ProjectionMatrix;
uniform mat4 ModelViewMatrix;
uniform float window_height;
uniform float window_width;
float PI = 3.14159;
float calcTriangleArea(float l[3]) //Heron's formula
{
float p = (l[0] + l[1] + l[2]) / 2.0;
return sqrt(p * (p - l[0]) * (p - l[1]) * (p - l[2]));
}
float calcSqureArea(vec4 position[4])
{
vec2 position_screen[4];
for(int i=0;i<4;i++)
{
position_screen[i] = position[i].xy;
}
float l[4];
for(int i = 0;i < 4;i++)
{
l[i] = length(position_screen[(i + 1) % 4] - position_screen[i % 4]);
}
float diagonal = length(position_screen[2] - position_screen[0]);
float l1[3];
float l2[3];
l1[0] = l[0];
l1[1] = l[1];
l1[2] = diagonal;
l2[0] = l[2];
l2[1] = l[3];
l2[2] = diagonal;
float area = calcTriangleArea(l1) + calcTriangleArea(l2);
return area;
}
float checkInsideView(vec4 position[4]) //check if the patch is visible
{
int flag = 4;
for(int i=0;i<4;i++)
{
if((position[i].x >= -window_width / 2.0) && (position[i].x <= window_width / 2.0) &&
(position[i].y >= -window_height / 2.0) && (position[i].y <= window_height / 2.0))
{
flag --;
}
}
if(flag == 0) //all 4 vertices are visible
{
return 0.0;
}
else if(flag == 4) //not all visible
{
return 2.0;
}
else //all vertices are not visible
{
return 1.0;
}
}
float calcLODLevel()
{
vec4 position_screen[4];
for(int i = 0; i < 4; i++)
{
position_screen[i] = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
}
float in_view_level = checkInsideView(position_screen);
//tess number is decided by the area that this patch covers on
//the screen
float area = calcSqureArea(position_screen);
float level = sqrt(area);
if(in_view_level == 1.0)
{
level /= sqrt(2);
}
//dont do tessellation
//if this patch is not visible
else if(in_view_level == 2.0)
{
level = 1.0;
}
return level;
}
void main()
{
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
float lod_level = calcLODLevel();
gl_TessLevelOuter[0] = lod_level;
gl_TessLevelOuter[1] = lod_level;
gl_TessLevelOuter[2] = lod_level;
gl_TessLevelOuter[3] = lod_level;
gl_TessLevelInner[0] = lod_level;
gl_TessLevelInner[1] = lod_level;
}
我认为问题出在你计算屏幕坐标,导致镶嵌层次太小。关键部分是这样的:
position_screen[i] = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
您在这里计算的是剪辑坐标,而不是屏幕坐标。要从剪辑坐标获取屏幕坐标,您需要:
- 进行透视分割。这会为您提供 [-1.0, 1.0]. 范围内的 NDC(归一化设备坐标)
- 从 NDC 计算屏幕坐标。
在代码中,计算可能如下所示:
vec4 posClip = ProjectionMatrix * ModelViewMatrix * gl_in[i].gl_Position;
vec2 posNdc = posClip.xy * (1.0 / posClip.w);
vec2 posScreen = 0.5 * (posNdc + 1.0) * vec2(window_width, window_height);