[OpenGL] 练习:简单的物体描边效果
开发环境:Qt, OpenGL
主要是为了练习glsl写的一个小例子,本身没有什么难度。
思路一:进行两次绘制。在顶点着色器中,第一次只绘制背面,将顶点沿着法线方向进行移动,可以使物体"膨胀"起来,使用描边的纯色填充,第一次渲出来的图像就是一个稍大一点的纯色物体。第二次正常绘制正面,此时尚未进行清屏,所以第二次绘制结果会叠加在第一次的结果上。
按照这一思路实现效果后,发现一个比较严重的缺陷,面与面之间法线差值较大的时候,会在顶点处产生缝隙,对于像正方体这样棱角比较分明的物体特别明显,所以之后又采用了第二种方法。
思路二:进行两次绘制。在顶点着色器中,第一次先沿视线方向,将物体靠近摄像机,然后再转换到投影空间,使用描边的纯色填充。第二次正常绘制正面。
如上图,第二种思路下,缝隙的现象已经好了不少,只是边的粗细在有些角度下有点不一致。
以下代码为沿法线膨胀的部分:
void MainWidget::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0, 0, 0, 1);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
QOpenGLShader* stroke_v_pass0 = new QOpenGLShader(QOpenGLShader::Vertex);
QOpenGLShader* stroke_v_pass1 = new QOpenGLShader(QOpenGLShader::Vertex);
QOpenGLShader* stroke_f = new QOpenGLShader(QOpenGLShader::Fragment);
stroke_v_pass0->compileSourceFile(":/stroke_v_pass0.glsl");
stroke_v_pass1->compileSourceFile(":/stroke_v_pass1.glsl");
stroke_f->compileSourceFile(":/stroke_f_pass0.glsl");
program.addShader(stroke_v_pass0);
program.addShader(stroke_f);
program.link();
program2.addShader(stroke_v_pass1);
program2.addShader(stroke_f);
program2.link();
geometries = new GeometryEngine;
timer.start(12, this);
}
void MainWidget::resizeGL(int w, int h)
{
float aspect = float(w) / float(h ? h : 1);
const qreal zNear = 1.0, zFar = 10.0, fov = 45.0;
projection.setToIdentity();
projection.perspective(fov, aspect, zNear, zFar);
}
void MainWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QVector3D trans(0,0, -7.0);
QMatrix4x4 mvMatrix;
mvMatrix.translate(trans);
mvMatrix.rotate(rotation);
glCullFace(GL_FRONT);
program.bind();
QMatrix4x4 IT_mvMatrix;
IT_mvMatrix = mvMatrix.transposed();
IT_mvMatrix = IT_mvMatrix.inverted();
program.setUniformValue("ModelViewMatrix", mvMatrix);
program.setUniformValue("IT_ModelViewMatrix", IT_mvMatrix);
program.setUniformValue("ProjectMatrix", projection);
QVector3D strokeColor = {1.0f,0.0f,0.0f};
program.setUniformValue("strokeColor", strokeColor);
geometries->drawCubeGeometry(&program);
glCullFace(GL_BACK);
program.release();
program2.bind();
program2.setUniformValue("ModelViewMatrix", mvMatrix);
program.setUniformValue("IT_ModelViewMatrix", IT_mvMatrix);
program2.setUniformValue("ProjectMatrix", projection);
QVector3D ambient = {0.2f,0.2f,0.2f};
program2.setUniformValue("ambient", ambient);
QVector3D LightLocation = {10.0f,9.0f,8.0f};
program2.setUniformValue("lightLocation", LightLocation);
QVector3D DiffuseColor = {0.8f,0.8f,0.8f};
program2.setUniformValue("diffuseColor", DiffuseColor);
QVector3D LightColor = {0.8f,0.8f,1.0f};
program2.setUniformValue("lightColor", LightColor);
geometries->drawCubeGeometry(&program2);
}
// stroke_v_pass0.glsl
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
uniform mat4 ModelViewMatrix;
uniform mat4 IT_ModelViewMatrix;
uniform mat4 ProjectMatrix;
uniform vec3 strokeColor;
attribute vec4 a_position;
attribute vec3 a_normal;
varying vec3 v_color;
void main()
{
vec3 normal = mat3(IT_ModelViewMatrix) * a_normal;
gl_Position = ModelViewMatrix * a_position;
gl_Position = ProjectMatrix * (gl_Position + normalize(vec4(normal, 0)) * 0.1);
v_color = strokeColor;
}
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
uniform mat4 ProjectMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 IT_ModelViewMatrix;
uniform vec3 ambient;
uniform vec3 lightLocation;
uniform vec3 diffuseColor;
uniform vec3 lightColor;
attribute vec4 a_position;
attribute vec3 a_normal;
varying vec3 v_color;
void main()
{
vec3 worldLightDir = normalize(lightLocation);
vec3 worldNormal = normalize(mat3(IT_ModelViewMatrix) * a_normal);
vec3 diffuse = diffuseColor * clamp(dot(worldNormal, worldLightDir),0.0,1.0);
gl_Position = ProjectMatrix * ModelViewMatrix * a_position;
v_color = diffuse + ambient;
}
// stroke_f_pass0.glsl
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
varying vec3 v_color;
void main()
{
gl_FragColor = vec4(v_color, 1.0f);
}