我目前正在将一个旧的OpenGL 1.1应用程序移植到OpenGL 3.0中,该应用程序使用线框模型。
在1.1中,以下代码用于创建虚线:
glPushAttrib(GL_ENABLE_BIT);
glLineStipple(1, 0x0F0F);
glEnable(GL_LINE_STIPPLE);
通常,将参数压入堆栈以影响随后的所有绘图操作。
我的问题:在不再使用此堆栈的OpenGL3中如何完成此操作?如何设置虚线(可能是在将坐标移交给glBufferData()之前?)?
答案 0 :(得分:2)
对于单独的线段,这一点都不复杂。例如,绘制GL_LINES
基元。
诀窍是要了解片段着色器中线段的起点。使用flat
插值限定符可以轻松实现。
顶点着色器必须将规范化的设备坐标传递给片段着色器。一次使用默认插值,一次不使用(flat
)插值。这导致在片段阴影中,第一个输入参数包含行上实际位置的NDC坐标,而后一个行起始处的NDC坐标。
#version 330
layout (location = 0) in vec3 inPos;
flat out vec3 startPos;
out vec3 vertPos;
uniform mat4 u_mvp;
void main()
{
vec4 pos = u_mvp * vec4(inPos, 1.0);
gl_Position = pos;
vertPos = pos.xyz / pos.w;
startPos = vertPos;
}
除了变化的输入外,片段着色器还具有统一变量。 u_resolution
包含视口的宽度和高度。 u_dashSize
包含行长,u_gapSize
包含像素间距。
因此可以计算从开始到实际片段的行长:
vec2 dir = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);
通过discard
命令可以丢弃间隙上的碎片。
if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
discard;
片段着色器:
#version 330
flat in vec3 startPos;
in vec3 vertPos;
out vec4 fragColor;
uniform vec2 u_resolution;
uniform float u_dashSize;
uniform float u_gapSize;
void main()
{
vec2 dir = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);
if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
discard;
fragColor = vec4(1.0);
}
对于以下简单的演示程序,我使用了GLFW API创建了一个窗口,使用了GLEW用于加载了OpenGL,并使用了GLM -OpenGL Mathematics用于数学。我没有提供功能CreateProgram
的代码,该函数只是从顶点着色器和片段着色器源代码创建一个程序对象:
#include <GL/glew.h>
#include <GL/gl.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <GLFW/glfw3.h>
#include <vector>
#define _USE_MATH_DEFINES
#include <math.h>
int main(void)
{
if (glfwInit() == GLFW_FALSE)
return 0;
GLFWwindow *window = glfwCreateWindow(400, 300, "OGL window", nullptr, nullptr);
if (window == nullptr)
return 0;
glfwMakeContextCurrent(window);
glewExperimental = true;
if (glewInit() != GLEW_OK)
return 0;
GLuint program = CreateProgram(vertShader, fragShader);
GLint loc_mvp = glGetUniformLocation(program, "u_mvp");
GLint loc_res = glGetUniformLocation(program, "u_resolution");
GLint loc_dash = glGetUniformLocation(program, "u_dashSize");
GLint loc_gap = glGetUniformLocation(program, "u_gapSize");
glUseProgram(program);
glUniform1f(loc_dash, 10.0f);
glUniform1f(loc_gap, 10.0f);
std::vector<float> varray{
-1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,
-1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1
};
std::vector<unsigned int> iarray{
0, 1, 1, 2, 2, 3, 3, 0,
4, 5, 5, 6, 6, 7, 7, 4,
0, 4, 1, 5, 2, 6, 3, 7
};
GLuint bo[2], vao;
glGenBuffers(2, bo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, bo[0] );
glBufferData(GL_ARRAY_BUFFER, varray.size()*sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, iarray.size()*sizeof(*iarray.data()), iarray.data(), GL_STATIC_DRAW);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 project;
int vpSize[2]{0, 0};
while (!glfwWindowShouldClose(window))
{
int w, h;
glfwGetFramebufferSize(window, &w, &h);
if (w != vpSize[0] || h != vpSize[1])
{
vpSize[0] = w; vpSize[1] = h;
glViewport(0, 0, vpSize[0], vpSize[1]);
project = glm::perspective(glm::radians(90.0f), (float)w/(float)h, 0.1f, 10.0f);
glUniform2f(loc_res, (float)w, (float)h);
}
static float angle = 1.0f;
glm::mat4 modelview( 1.0f );
modelview = glm::translate(modelview, glm::vec3(0.0f, 0.0f, -3.0f) );
modelview = glm::rotate(modelview, glm::radians(angle), glm::vec3(1.0f, 0.0f, 0.0f));
modelview = glm::rotate(modelview, glm::radians(angle*0.5f), glm::vec3(0.0f, 1.0f, 0.0f));
angle += 0.5f;
glm::mat4 mvp = project * modelview;
glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
glClear(GL_COLOR_BUFFER_BIT);
glDrawElements(GL_LINES, (GLsizei)iarray.size(), GL_UNSIGNED_INT, nullptr);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
如果目标是沿多边形绘制虚线,则情况会变得更加复杂。例如,绘制GL_LINE_STRIP
原语。
如果不知道该行的所有图元,则无法在着色器程序中计算该行的长度。即使所有原语都是已知的(例如SSBO),也必须在循环中进行计算。
我决定向着色器程序添加一个附加属性,该属性包含从线的起点到顶点坐标的“距离”。 “距离”是指投影到视口上的多边形的长度。
这将导致顶点着色器和片段着色器更加简单:
顶点着色器:
#version 330
layout (location = 0) in vec3 inPos;
layout (location = 1) in float inDist;
out float dist;
uniform mat4 u_mvp;
void main()
{
dist = inDist;
gl_Position = u_mvp * vec4(inPos, 1.0);
}
片段着色器:
#version 330
in float dist;
out vec4 fragColor;
uniform vec2 u_resolution;
uniform float u_dashSize;
uniform float u_gapSize;
void main()
{
if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
discard;
fragColor = vec4(1.0);
}
在演示程序中,inDist
属性是在CPU上计算的。每个顶点坐标都通过模型,视图,投影矩阵进行转换。最后,它从规范化的设备空间转换为窗口空间。计算线带相邻坐标之间的XY距离,并沿着线带求和长度并将其分配给相应的属性值:
int w = [...], h = [...]; // window widht and height
glm::mat4 mpv = [...]; // model view projection matrix
std::vector<glm::vec3> varray{ [...] }; // array of vertex
std::vector<float> darray(varray.size(), 0.0f); // distance attribute - has to be computed
glm::mat4 wndmat = glm::scale(glm::mat4(1.0f), glm::vec3((float)w/2.0f, (float)h/2.0f, 1.0f));
wndmat = glm::translate(wndmat, glm::vec3(1.0f, 1.0f, 0.0f));
glm::vec2 vpPt(0.0f, 0.0f);
float dist = 0.0f;
for (size_t i=0; i < varray.size(); ++i)
{
darray[i] = dist;
glm::vec4 clip = mvp * glm::vec4(varray[i], 1.0f);
glm::vec4 ndc = clip / clip.w;
glm::vec4 vpC = wndmat * ndc;
float len = i==0 ? 0.0f : glm::length(vpPt - glm::vec2(vpC));
vpPt = glm::vec2(vpC);
dist += len;
}
演示程序:
int main(void)
{
if (glfwInit() == GLFW_FALSE)
return 0;
GLFWwindow *window = glfwCreateWindow(800, 600, "OGL window", nullptr, nullptr);
if (window == nullptr)
return 0;
glfwMakeContextCurrent(window);
glewExperimental = true;
if (glewInit() != GLEW_OK)
return 0;
GLuint program = CreateProgram(vertShader, fragShader);
GLint loc_mvp = glGetUniformLocation(program, "u_mvp");
GLint loc_res = glGetUniformLocation(program, "u_resolution");
GLint loc_dash = glGetUniformLocation(program, "u_dashSize");
GLint loc_gap = glGetUniformLocation(program, "u_gapSize");
glUseProgram(program);
glUniform1f(loc_dash, 10.0f);
glUniform1f(loc_gap, 10.0f);
std::vector<glm::vec3> varray;
for (size_t u=0; u <= 360; ++u)
{
double a = u*M_PI/180.0;
double c = cos(a), s = sin(a);
varray.emplace_back(glm::vec3((float)c, (float)s, 0.0f));
}
std::vector<float> darray(varray.size(), 0.0f);
GLuint bo[2], vao;
glGenBuffers(2, bo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, bo[0] );
glBufferData(GL_ARRAY_BUFFER, varray.size()*sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, bo[1] );
glBufferData(GL_ARRAY_BUFFER, darray.size()*sizeof(*darray.data()), darray.data(), GL_STATIC_DRAW);
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 project, wndmat;
int vpSize[2]{0, 0};
while (!glfwWindowShouldClose(window))
{
int w, h;
glfwGetFramebufferSize(window, &w, &h);
if (w != vpSize[0] || h != vpSize[1])
{
vpSize[0] = w; vpSize[1] = h;
glViewport(0, 0, vpSize[0], vpSize[1]);
project = glm::perspective(glm::radians(90.0f), (float)w/(float)h, 0.1f, 10.0f);
glUniform2f(loc_res, (float)w, (float)h);
wndmat = glm::scale(glm::mat4(1.0f), glm::vec3((float)w/2.0f, (float)h/2.0f, 1.0f));
wndmat = glm::translate(wndmat, glm::vec3(1.0f, 1.0f, 0.0f));
}
static float angle = 1.0f;
glm::mat4 modelview( 1.0f );
modelview = glm::translate(modelview, glm::vec3(0.0f, 0.0f, -2.0f) );
modelview = glm::rotate(modelview, glm::radians(angle), glm::vec3(1.0f, 0.0f, 0.0f));
modelview = glm::rotate(modelview, glm::radians(angle*0.5f), glm::vec3(0.0f, 1.0f, 0.0f));
angle += 0.5f;
glm::mat4 mvp = project * modelview;
glm::vec2 vpPt(0.0f, 0.0f);
float dist = 0.0f;
for (size_t i=0; i < varray.size(); ++i)
{
darray[i] = dist;
glm::vec4 clip = mvp * glm::vec4(varray[i], 1.0f);
glm::vec4 ndc = clip / clip.w;
glm::vec4 vpC = wndmat * ndc;
float len = i==0 ? 0.0f : glm::length(vpPt - glm::vec2(vpC));
vpPt = glm::vec2(vpC);
dist += len;
}
glBufferSubData(GL_ARRAY_BUFFER, 0, darray.size()*sizeof(*darray.data()), darray.data());
glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)varray.size());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}