1. 程式人生 > >[OpenGL] 簡單的物體描邊效果

[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);
}