qt opengl 双面光照

30 篇文章 10 订阅

    双面光照是为了应对像茶壶这种有内壳的物体,如果只才用一个正面光照的话,那么内壳里面就是黑的,显得不真实。

其实现原理是非常简单的;就是在前面光照的基础上,计算一次原法向量的反法向量光照参数,并通过gl_FrontFacing参数来使用正面光照的因子,不是gl_FrontFacing的使用反面光照因子,如此而已。

     我做用的就是一个茶壶模型,效果如下。

不采用双面光照时

采用双面光照时

我使用的是obj模型,计算出了其每个点的平均面法向量,我的obj文件格式如下

# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# 创建的文件:06.06.2015 18:17:22
#
# object _null_
#
v  4.5315 5.2235 -0.0798
v  4.3990 5.2640 1.1075
v  4.3506 5.4725 1.0869
v  4.4814 5.4325 -0.0847
v  4.3581 5.5980 1.0864
v  4.4892 5.5579 -0.0876
v  4.4071 5.6404 1.1014
v  4.5399 5.5998 -0.0886

.....

...

....只有顶点坐标

# 888 vertices

g _null_ //以下为三角形面索引
f 1 2 3 
f 3 4 1 
f 4 3 5 
f 5 6 4 
f 6 5 7 
f 7 8 6 
f 8 7 9 
f 9 10 8 
f 10 9 11 
f 11 12 10 
f 12 11 13 
f 13 14 12 
f 2 15 16 
f 16 3 2 

 ...

...

...

我加载的方法如下

bool ObjLoader::load(QString fileName, QVector<float> &vPoints,QVector<float> &nPoints)
{
    if(fileName.mid(fileName.lastIndexOf('.')) != ".obj"){
        qDebug() << "file is not a obj file";
        return false;
    }

    QFile objFile(fileName);
    if(!objFile.open(QIODevice::ReadOnly)){
        qDebug() << "open" << fileName << "failed";
        return false;
    }

    QVector<float> vertextPoints;
    QVector<int> faceIndexs;
    QHash<int,QSet<QVector3D>> normalHash;
    while (!objFile.atEnd()) {
        QByteArray lineData = objFile.readLine().simplified();
        if(lineData.isEmpty())continue;

        QList<QByteArray> strValues = lineData.split(' ');
        QString dataType = strValues.takeFirst();
        if(dataType == "v"){ //将顶点全部取出
            std::transform(strValues.begin(),strValues.end(),std::back_inserter(vertextPoints),[](QByteArray &str){
                return str.toFloat();
            });
        }else if(dataType == "f"){
            if(strValues.count() != 3){
                continue;
            }

            QVector<int> indexs;
            indexs << strValues.first().toInt() << strValues.at(1).toInt() << strValues.last().toInt();
            faceIndexs << indexs;
            QVector3D point1(vertextPoints.at((indexs.first() - 1) * 3),
                            vertextPoints.at((indexs.first() - 1) * 3 + 1),
                            vertextPoints.at((indexs.first() - 1) * 3 + 2));

            QVector3D point2(vertextPoints.at((indexs.at(1) - 1) * 3),
                            vertextPoints.at((indexs.at(1) - 1) * 3 + 1),
                            vertextPoints.at((indexs.at(1) - 1) * 3 + 2));

            QVector3D point3(vertextPoints.at((indexs.last() - 1) * 3),
                            vertextPoints.at((indexs.last() - 1) * 3 + 1),
                            vertextPoints.at((indexs.last() - 1) * 3 + 2));
            QVector3D side12 = point2 - point1;
            QVector3D side13 = point3 - point1; //根据三角形两边向量的叉积求出法向量,并放入集合
            QVector3D vNormal = QVector3D::crossProduct(side12,side13);
            for(auto &tempIndex : indexs){
                normalHash[tempIndex].insert(vNormal);
            }
        }
    }
    objFile.close();

    for(int i = 0; i < faceIndexs.count(); i++){ //遍历索引,找出需要画的顶点
        vPoints << vertextPoints.at((faceIndexs.at(i) - 1) * 3) << vertextPoints.at((faceIndexs.at(i) - 1) * 3 + 1) << vertextPoints.at((faceIndexs.at(i) - 1) * 3 + 2);
        QSet<QVector3D> normalsIndex = normalHash.value(faceIndexs.at(i));
        QVector3D averateNormal; //根据当前索引,求出该点的法向量平均值
        foreach (const QVector3D &value, normalsIndex){
            averateNormal += value;
        }
        averateNormal.normalize();
        nPoints << averateNormal.x() << averateNormal.y() << averateNormal.z();
    }
}

 其渲染器和前一篇非常一样了

#ifndef DOUBLELIGHTOBJRENDER_H
#define DOUBLELIGHTOBJRENDER_H

#include <QOpenGLShaderProgram>
#include <QOpenGLExtraFunctions>
#include <QOpenGLTexture>
#include <QOpenGLBuffer>
class DoubleLightObjRender
{
public:
    DoubleLightObjRender() = default;
    void initsize(QString filename);
    void render(QOpenGLExtraFunctions *f,QMatrix4x4 &pMatrix,QMatrix4x4 &vMatrix,QMatrix4x4 &mMatrix,QVector3D &cameraLocation,QVector3D &lightCation);

private:
    QOpenGLBuffer vbo_;
    QOpenGLShaderProgram program_;
    QVector<float> vertPoints_,normalPoints_;
};

#endif // DOUBLELIGHTOBJRENDER_H
#include "doublelightobjrender.h"
#include "utils/objloader.h"
void DoubleLightObjRender::initsize(QString filename)
{
    program_.addCacheableShaderFromSourceFile(QOpenGLShader::Vertex,"vsrc.vert");
    program_.addCacheableShaderFromSourceFile(QOpenGLShader::Fragment,"fsrc.frag");
    program_.link();

    ObjLoader objModelLoader;
    objModelLoader.load(filename,vertPoints_,normalPoints_);
    QVector<float> points;
    points << vertPoints_ << normalPoints_;
    vbo_.create();
    vbo_.bind();
    vbo_.allocate(points.data(),points.count() * sizeof(float));
}

void DoubleLightObjRender::render(QOpenGLExtraFunctions *f, QMatrix4x4 &pMatrix, QMatrix4x4 &vMatrix, QMatrix4x4 &mMatrix, QVector3D &cameraLocation, QVector3D &lightCation)
{
    f->glEnable(GL_DEPTH_TEST);
    f->glDisable(GL_CULL_FACE);
    program_.bind();
    vbo_.bind();
    program_.setUniformValue("uPMatrix",pMatrix);
    program_.setUniformValue("uVMatrix",vMatrix);
    program_.setUniformValue("uMMatrix",mMatrix);
    program_.setUniformValue("uLightLocation",lightCation);
    program_.setUniformValue("uCamera",cameraLocation);

    program_.enableAttributeArray(0);
    program_.enableAttributeArray(1);
    program_.setAttributeBuffer(0,GL_FLOAT,0,3,3*sizeof(GLfloat));
    program_.setAttributeBuffer(1,GL_FLOAT,vertPoints_.count() * sizeof(GLfloat),3,3*sizeof(GLfloat));
    f->glDrawArrays(GL_TRIANGLES,0,vertPoints_.count()/3);

    program_.disableAttributeArray(0);
    program_.disableAttributeArray(1);
    vbo_.release();
    program_.release();
    f->glDisable(GL_DEPTH_TEST);
}

其shader有些不同,多算了一次反面光照因子

#version 330
uniform mat4 uPMatrix,uVMatrix,uMMatrix;
uniform vec3 uLightLocation,uCamera;
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec3 aNormal;
smooth out vec4 vAmbientZM;
smooth out vec4 vDiffuseZM;
smooth out vec4 vSpecularZM;
smooth out vec4 vAmbientFM;
smooth out vec4 vDiffuseFM;
smooth out vec4 vSpecularFM;

void pointLight(in vec3 normal,inout vec4 ambient,inout vec4 diffuse,inout vec4 specular,in vec4 lightAmbient,in vec4 lightDiffuse,in vec4 lightSpecular,in float shininess){
    ambient = lightAmbient;

    vec3 normalTarget = aPosition + normal;
    vec3 newNormal = normalize((uMMatrix * vec4(normalTarget,1)).xyz - (uMMatrix * vec4(aPosition,1)).xyz);
    vec3 eye = normalize(uCamera - (uMMatrix * vec4(aPosition,1)).xyz);
    vec3 vp = normalize(uLightLocation - (uMMatrix * vec4(aPosition,1)).xyz);
    vec3 halfVector = normalize(eye + vp);

    float nDotViewPotision = max(0.0,dot(newNormal,vp));
    diffuse = lightDiffuse * nDotViewPotision;

    float nDotViewHalfVector = dot(newNormal,halfVector);
    float powerFactor = max(0.0,pow(nDotViewHalfVector,shininess));
    specular = lightSpecular * powerFactor;
}

void main(void)
{
    gl_Position = uPMatrix * uVMatrix * uMMatrix * vec4(aPosition,1);
    vec4 ambient = vec4(0.0,0.0,0.0,0.0),diffuse = vec4(0.0,0.0,0.0,0.0),specular = vec4(0.0,0.0,0.0,0.0);
    pointLight(aNormal,ambient,diffuse,specular,vec4(0.1,0.1,0.1,1.0),vec4(0.7,0.7,0.7,1.0),vec4(0.3,0.3,0.3,1),50);
    vAmbientZM = ambient;
    vDiffuseZM = diffuse;
    vSpecularZM = specular;//传入-aNormal就可以求出另一面的光照因子
    pointLight(-aNormal,ambient,diffuse,specular,vec4(0.1,0.1,0.1,1.0),vec4(0.7,0.7,0.7,1.0),vec4(0.3,0.3,0.3,1),50);
    vAmbientFM = ambient;
    vDiffuseFM = diffuse;
    vSpecularFM = specular;
}
#version 330
in vec4 vAmbientZM;
in vec4 vDiffuseZM;
in vec4 vSpecularZM;
in vec4 vAmbientFM;
in vec4 vDiffuseFM;
in vec4 vSpecularFM;
out vec4 fragColor;

void main(void)
{
    vec4 finalColor = vec4(0.7,0.5,0.2,1.0);
    if(gl_FrontFacing){ //是正面
        finalColor = finalColor * (vAmbientZM + vDiffuseZM + vSpecularZM);
    }else{//是反面
        finalColor = finalColor * (vAmbientFM + vDiffuseFM + vSpecularFM);
    }
    fragColor = finalColor;
}

其使用代码和前面一样

#ifndef WIDGET_H
#define WIDGET_H

#include <QOpenGLWidget>
#include <QTimer>
#include "doublelightobjrender.h"

class Widget : public QOpenGLWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

protected:
    void initializeGL() override;
    void resizeGL(int w,int h) override;
    void paintGL() override;

private:
    DoubleLightObjRender render_;
    QTimer tm_;
    QVector3D cameraLocation_,lightLocation_;
    QMatrix4x4 pMatrix_;
    qreal angleX_,angleY,anglZ_;

private slots:
    void slotTimeout();
};

#endif // WIDGET_H
#include "widget.h"
#include "utils/objloader.h"

Widget::Widget(QWidget *parent)
    : QOpenGLWidget(parent)
{
    connect(&tm_,SIGNAL(timeout()),this,SLOT(slotTimeout()));
//    tm_.start(30);
}

Widget::~Widget()
{

}

void Widget::initializeGL()
{
    render_.initsize("ch.obj");
    cameraLocation_.setX(0);
    cameraLocation_.setY(20);
    cameraLocation_.setZ(10);
    lightLocation_.setX(20);
    lightLocation_.setY(20);
    lightLocation_.setZ(20);
}

void Widget::resizeGL(int w, int h)
{
    pMatrix_.setToIdentity();
    pMatrix_.perspective(45,float(w)/h,0.01f,100.0f);
}

void Widget::paintGL()
{
    QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
    f->glClearColor(0.0f,0.0f,0.0f,1.0f);
    f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QMatrix4x4 vMatrix;
    vMatrix.lookAt(cameraLocation_,QVector3D(0,0,0),QVector3D(0,0,-1));

    QMatrix4x4 mMatrix;
    mMatrix.translate(-1,0,0);
//    mMatrix.rotate(angleX_,1,0,0);
    mMatrix.rotate(angleY,0,1,0);
//    mMatrix.rotate(anglZ_,0,0,1);
    render_.render(f,pMatrix_,vMatrix,mMatrix,cameraLocation_,lightLocation_);
}

void Widget::slotTimeout()
{
    angleX_ += 5;
    angleY += 5;
    anglZ_ += 5;
    update();
}

到此结束了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值