双面光照是为了应对像茶壶这种有内壳的物体,如果只才用一个正面光照的话,那么内壳里面就是黑的,显得不真实。
其实现原理是非常简单的;就是在前面光照的基础上,计算一次原法向量的反法向量光照参数,并通过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();
}
到此结束了。