[OpenGL] 簡單的物體描邊效果
阿新 • • 發佈:2018-12-13
開發環境: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);
}