前面的tinyrender中心任务就是写像素,这也是opengl的中心任务,这里用QImage来写像素,和Qt的GUI交互,来用our_gl代替实际的OpenGL来实现Qt的hellogl程序用于调试。 由于是软光栅器,小的obj模型还是能流畅旋转的,这里的shader是模拟了diffuse和specu光照.
还是粘贴的修改过的代码文件.
our_gl.h
主要增加了
void glMatrixMode(unsigned int mode);
void glLoadIdentity();
void glTranslatef(float x, float y, float z);
void glRotatef(float angle, float x, float y, float z);
几个函数的实现
#ifndef __OUR_GL_H__
#define __OUR_GL_H__
#include "tgaimage.h"
#include <QImage>
#include "geometry.h"
#include "model.h"
#define TI_GL_COLOR_BUFFER_BIT 0x01
#define TI_GL_DEPTH_BUFFER_BIT 0x02
#define TI_GL_DEPTH_TEST 0x01
#define TI_GL_PROJECTION 0x01
#define TI_GL_MODELVIEW 0x02
extern Matrix ModelView;
extern Matrix Viewport;
extern Matrix Projection;
struct IShader {
virtual ~IShader() = 0;
virtual Vec3i vertex(int iface, int nthvert) = 0;
virtual bool fragment(Vec3f bar, TGAColor &color) = 0;
Vec3i frag_coord;
};
namespace our_gl
{
void glutInit(int w, int h, QImage* colorImage, QImage* zImage, IShader* defaultShader);
void glutFinal();
void glOrtho(float l, float r, float b, float t, float n, float f);
void glFrustum(float l, float r, float b, float t, float n, float f);
void gluLookAt(Vec3f eye, Vec3f center, Vec3f up);
void glViewport(int x, int y, int w, int h);
void glDrawELements(Model* m);
void glClear(unsigned int flag);
void glEnable(unsigned int flag);
void glDisable(unsigned int flag);
void glMatrixMode(unsigned int mode);
void glLoadIdentity();
void glTranslatef(float x, float y, float z);
void glRotatef(float angle, float x, float y, float z);
}
#endif //__OUR_GL_H__
our_gl.cpp
#include <cmath>
#include <limits>
#include "our_gl.h"
#include <QImage>
#ifndef M_PI
# define M_PI 3.14159265358979323846 /* pi */
#endif
Matrix ModelView;
Matrix Viewport;
Matrix Projection;
QImage* _colorImage = nullptr;
QImage*_zImage = nullptr;
float* _zbufreal = nullptr;
QColor clearColor(102,102,204);
TGAColor depthValue(255);
float depthvreal = 255.f;
unsigned int testflag = 0;
IShader* _defaultShader = nullptr;
Matrix* _curMatrix = nullptr;
int _buf_width = 0;
int _buf_height = 0;
void triangle(Vec3i *pts, IShader &shader, QImage &colorbuffer, QImage &zbuffer, float* zbuf_real, unsigned int fragmentTestFalg = 0);
template <typename T> T CLAMP(const T& value, const T& low, const T& high)
{
return value < low ? low : (value > high ? high : value);
}
IShader::~IShader() {
}
namespace our_gl
{
void glutInit(int w, int h, QImage* colorImage, QImage* zImage, IShader* defaultShader)
{
_buf_width = w;
_buf_height = h;
_zbufreal = new float[w*h];
_colorImage = colorImage;
_zImage = zImage;
_defaultShader = defaultShader;
_curMatrix = &Projection;
}
void glutFinal()
{
if(_zbufreal)
{
delete[] _zbufreal;
}
}
void glViewport(int x, int y, int w, int h)
{
Viewport = Matrix::identity();
Viewport[0][3] = x+w/2.f;
Viewport[1][3] = y+h/2.f;
Viewport[2][3] = 255.f/2.f;
Viewport[0][0] = w/2.f;
Viewport[1][1] = h/2.f;
Viewport[2][2] = 255.f/2.f;
_buf_width = w;
_buf_height = h;
if (_zbufreal)
{
delete[] _zbufreal;
_zbufreal = new float[w*h];
}
}
void glOrtho(float l, float r, float b, float t, float n, float f)
{
//Projection = Matrix::identity();
//auto& m = Projection;
auto& m = *_curMatrix;
m[0][0] = (2.f) / (r-l);
m[1][1] = (2.f) / (t-b);
m[2][2] = (-2.f) / (f-n);
m[0][3] = (r+l) / (r-l);
m[1][3] = (t+b) / (t-b);
m[2][3] = -(f+n) / (f-n);
}
void glFrustum(float l, float r, float b, float t, float n, float f)
{
//Projection = Matrix::identity();
//auto& m = Projection;
auto& m = *_curMatrix;
m[0][0] = (2.f*n) / (r-l);
m[1][1] = (2.f*n) / (t-b);
m[0][2] = (r+l) / (r-l);
m[1][2] = (t+b) / (t-b);
m[2][2] = -(f+n) / (f-n);
m[3][2] = -1.f;
m[2][3] = -(2.f*f*n) / (f-n);
}
void gluLookAt(Vec3f eye, Vec3f center, Vec3f up)
{
Vec3f z = (eye-center).normalize();
Vec3f x = cross(up,z).normalize();
Vec3f y = cross(z,x).normalize();
ModelView = Matrix::identity();
auto& m = ModelView;
for (int i=0; i<3; i++)
{
m[0][i] = x[i];
m[1][i] = y[i];
m[2][i] = z[i];
}
m[0][3] = -(eye*x);
m[1][3] = -(eye*y);
m[2][3] = -(eye*z);
}
void glClear(unsigned int flag)
{
if (flag & TI_GL_COLOR_BUFFER_BIT)
{
// int w = _colorImage->get_width();
// int h = _colorImage->get_height();
// for (int i = 0; i < h; i++)
// {
// for (int j = 0; j < w; j++)
// {
// _colorImage->set(i,j, clearColor);
// }
// }
// _colorImage->fill(QColor(clearColor[0],clearColor[1],clearColor[2]));
_colorImage->fill(clearColor);
}
if (flag & TI_GL_DEPTH_BUFFER_BIT)
{
int w = _zImage->width();
int h = _zImage->height();
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
// _zImage->set(i,j, depthValue);
_zbufreal[i*w+j] = depthvreal;
}
}
_zImage->fill(QColor(depthValue[0],depthValue[0],depthValue[0]));
}
}
void glEnable(unsigned int flag)
{
testflag |= flag;
}
void glDisable(unsigned int flag)
{
testflag &= ~flag;
}
void glMatrixMode(unsigned int mode)
{
if (mode == TI_GL_MODELVIEW)
{
_curMatrix = &ModelView;
}
else
{
_curMatrix = &Projection;
}
}
void glLoadIdentity()
{
(*_curMatrix) = Matrix::identity();
}
void glTranslatef(float x, float y, float z)
{
Matrix m = Matrix::identity();
m[0][3] = x;
m[1][3] = y;
m[2][3] = z;
(*_curMatrix) = (*_curMatrix) * m;
}
void glRotatef(float angle, float x, float y, float z)
{
Vec3f r = Vec3f(x,y,z).normalize();
float rx = r[0];
float ry = r[1];
float rz = r[2];
float rx2 = rx*rx;
float ry2 = ry*ry;
float rz2 = rz*rz;
float si = (M_PI*angle) / 180.f;
float cos_si = cos(si);
float sin_si = sin(si);
Matrix m = Matrix::identity();
m[0][0] = cos_si + rx2 *(1.f-cos_si);
m[0][1] = rx*ry*(1.f-cos_si) - rz*sin_si;
m[0][2] = rx*rz*(1.f-cos_si) + ry*sin_si;
m[1][0] = ry*rx*(1.f-cos_si) + rz*sin_si;
m[1][1] = cos_si + ry2 *(1.f-cos_si);
m[1][2] = ry*rz*(1.f-cos_si) - rx*sin_si;
m[2][0] = rz*rx*(1.f-cos_si) - ry*sin_si;
m[2][1] = rz*ry*(1.f-cos_si) + rx*sin_si;
m[2][2] = cos_si + rz2 *(1.f-cos_si);
(*_curMatrix) = (*_curMatrix) * m;
}
void glDrawELements(Model* m)
{
for (int i=0; i<m->nfaces(); i++) {
Vec3i screen_coords[3];
for (int j=0; j<3; j++) {
screen_coords[j] = _defaultShader->vertex(i, j);
}
triangle(screen_coords, *_defaultShader, *_colorImage, *_zImage, _zbufreal,testflag);
}
}
}//end of namespace ti
Vec3f barycentric(Vec3i A, Vec3i B, Vec3i C, Vec3i P) {
Vec3i s[2];
for (int i=2; i--; ) {
s[i][0] = C[i]-A[i];
s[i][1] = B[i]-A[i];
s[i][2] = A[i]-P[i];
}
Vec3f u = cross(s[0], s[1]);
if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate
return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z);
return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator
}
// triangle是三角形光栅化过程,隐蔽在GPU中,程序员接触不到
void triangle(Vec3i *pts, IShader &shader, QImage &colorbuffer, QImage &zbuffer, float* zbuf_real, unsigned int fragmentTestFlag) {
// pts为经过 顶点着色器后三角形三个顶点的屏幕+深度坐标(整形,x,y为像素坐标,z为深度)
// 计算pts三角形在屏幕上的矩形区域
// boxmax
// *---*---*
// | / \ |
// | / \ |
// |/ \|
// *-------*
//boxmin
Vec2i bboxmin( std::numeric_limits<int>::max(), std::numeric_limits<int>::max());
Vec2i bboxmax(-std::numeric_limits<int>::max(), -std::numeric_limits<int>::max());
for (int i=0; i<3; i++) {
for (int j=0; j<2; j++) {
bboxmin[j] = std::min(bboxmin[j], pts[i][j]);
bboxmax[j] = std::max(bboxmax[j], pts[i][j]);
}
}
// 两个for遍历该矩形区域的所有像素P
// boxmax
// *---*---*
// | / \ |
// | / \ |
// |/ *p\|
// *-------*
//boxmin
Vec3i P;
TGAColor color;
for (P.x=bboxmin.x; P.x<=bboxmax.x; P.x++) {
for (P.y=bboxmin.y; P.y<=bboxmax.y; P.y++) {
// 计算P在屏幕三角形上的重心坐标
Vec3f c = barycentric(pts[0], pts[1], pts[2], P);
// 插值深度值, 得到P的深度值
float p_depth = pts[0].z*c.x + pts[1].z*c.y + pts[2].z*c.z;
// 由于深度buffer是用一张灰度图片存储的,要clamping到0-155
P.z = std::max(0, std::min(255, int(p_depth + .5)));
//P.z = std::max(0, std::min(255, int(pts[0].z*c.x + pts[1].z*c.y + pts[2].z*c.z + .5))); // clamping to 0-255 since it is stored in unsigned char
//if (c.x<0 || c.y<0 || c.z<0 || zbuffer.get(P.x, P.y)[0]>P.z) continue;
// 坐标的barycenti c都大于0, 则像素在三角形里面
// 否则略过该像素
if (c.x<0 || c.y<0 || c.z<0)
{
continue;
}
// 片元处理
shader.frag_coord = P;
bool discard = shader.fragment(c, color);
if (discard) {
continue;
}
float& pre_frag_d_r = zbuf_real[P.y*zbuffer.width() + P.x];
if (fragmentTestFlag& TI_GL_DEPTH_TEST)
{
// zbuffer的该像素位置的深度值,前一个片元的深度
//int pre_fragment_depth = zbuffer.get(P.x,P.y)[0];
// 如果P的深度比该屏幕位置的前一个片元的深度大
// 这里z指向眼睛, P.z其实是远裁剪面的距离
// 越深则z值越小
// if (pre_fragment_depth > P.z)
//if (P.z >= pre_fragment_depth)
if (p_depth >=pre_frag_d_r)
{
continue;
}
}
//zbuffer.set(P.x, P.y, TGAColor(P.z));
zbuffer.setPixelColor(P.x,_buf_height - P.y -1,QColor(P.z,P.z,P.z));
pre_frag_d_r = p_depth;
//image.set(P.x, P.y, color);
int r = color[0];
int g = color[1];
int b = color[2];
colorbuffer.setPixelColor(P.x,_buf_height - P.y -1,QColor(r,g,b));
}
}
}
glwidget.h
大部分opengl代码都是Qt5 的hellogl例子
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QWidget>
#include <QResizeEvent>
class Model;
class GouraudShader;
class GLWidget : public QWidget
{
Q_OBJECT
public:
GLWidget(QWidget *parent = 0);
~GLWidget();
QSize minimumSizeHint() const;
QSize sizeHint() const;
public slots:
void setXRotation(int angle);
void setYRotation(int angle);
void setZRotation(int angle);
signals:
void xRotationChanged(int angle);
void yRotationChanged(int angle);
void zRotationChanged(int angle);
protected:
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
void updateGL();
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
private:
int xRot;
int yRot;
int zRot;
QPoint lastPos;
QColor qtGreen;
QColor qtPurple;
QImage* colorImage;
QImage* depthImage;
Model* model;
GouraudShader* shader;
};
#endif
glwidget.cpp
#include "glwidget.h"
#include <math.h>
#include <QPainter>
#include "tgaimage.h"
#include "model.h"
#include "geometry.h"
#include "our_gl.h"
using namespace our_gl;
//-----------------------------
//- default shader
class GouraudShader : public IShader
{
public:
float SpecContri;
float DiffContri;
Vec3f varying_intensity; // written by vertex shader, read by fragment shader
// 三角形顶点.glsl是自动的
Vec3f tri_vert[3];
Model* modelptr;
Vec3f light_dir;
GouraudShader()
{
light_dir = Vec3f(1.f,1.f,1.f);
light_dir.normalize();
SpecContri = 0.3f;
DiffContri = 1.f - SpecContri;
}
Vec3f reflectDir(Vec3f& n, Vec3f& v)
{
return n * (2 * (v * n)) - v;
}
virtual Vec3i vertex(int iface, int nthvert) {
Vec4f gl_Vertex = embed<4>(modelptr->vert(iface, nthvert)); // read the vertex from .obj file
gl_Vertex = Viewport*Projection*ModelView*gl_Vertex; // transform it to screen coordinates
Vec4f normal = embed<4>(modelptr->normal(iface, nthvert));
normal[3] = 0.f;
normal = ModelView * normal;
Vec3f normal_dir(normal[0], normal[1], normal[2]);
Vec3f refect_dir = reflectDir(normal_dir, light_dir);
float diffuse = std::max(0.f, normal_dir * light_dir);
float spec = 0.f;
Vec3f toeye_dir(0.f, 0.f, 1.f);
if (diffuse > 0.f)
{
spec = std::max(0.f, refect_dir * toeye_dir);
spec = pow(spec, 16.f);
}
float LightIntensity = DiffContri * diffuse + SpecContri * spec;
varying_intensity[nthvert] = LightIntensity;
//三角形的顶点存储, glsl是自动的
tri_vert[nthvert] = modelptr->vert(iface, nthvert);
return proj<3>(gl_Vertex/gl_Vertex[3]); // project homogenious coordinates to 3d
}
virtual bool fragment(Vec3f bar, TGAColor &color) {
int xi = frag_coord[0]%8;
int yi = frag_coord[1]%8;
float intensity = varying_intensity*bar; // interpolate intensity for the current pixel
TGAColor ambicolor_ratio(58, 58, 50);
color = TGAColor(255, 255, 255)*intensity; // well duh
TGAColor ambicolor(0, 0, 0);
ambicolor = ambicolor_ratio;
int cr = int(color[0])+ int(ambicolor[0]);
if (cr > 255) cr=255;
int cg = int(color[1])+ int(ambicolor[1]);
if (cg > 255) cg=255;
int cb = int(color[2])+ int(ambicolor[2]);
if (cb > 255) cb=255;
color = TGAColor(cr,cg,cb);
return false; // no, we do not discard this pixel
}
};
//-----------------------------
//- from Qt example hello gl
GLWidget::GLWidget(QWidget *parent)
: QWidget(parent)
{
xRot = 0;
yRot = 0;
zRot = 0;
initializeGL();
}
GLWidget::~GLWidget()
{
delete colorImage;
delete depthImage;
delete model;
delete shader;
glutFinal();
}
QSize GLWidget::minimumSizeHint() const
{
return QSize(50, 50);
}
QSize GLWidget::sizeHint() const
{
return QSize(400, 400);
}
static void qNormalizeAngle(int &angle)
{
while (angle < 0)
angle += 360 * 16;
while (angle > 360 * 16)
angle -= 360 * 16;
}
void GLWidget::setXRotation(int angle)
{
qNormalizeAngle(angle);
if (angle != xRot) {
xRot = angle;
emit xRotationChanged(angle);
updateGL();
}
}
void GLWidget::setYRotation(int angle)
{
qNormalizeAngle(angle);
if (angle != yRot) {
yRot = angle;
emit yRotationChanged(angle);
updateGL();
}
}
void GLWidget::setZRotation(int angle)
{
qNormalizeAngle(angle);
if (angle != zRot) {
zRot = angle;
emit zRotationChanged(angle);
updateGL();
}
}
void GLWidget::initializeGL()
{
shader = new GouraudShader();
int w = 100;
int h = 100;
colorImage = new QImage(100,100,QImage::Format_RGB888);
depthImage = new QImage(100,100,QImage::Format_RGB888);
glutInit(w, h, colorImage, depthImage, shader);
//构造vbo model阶段
model = new Model("f:/obj/african_head.obj");
shader->modelptr = model;
//qglClearColor(qtPurple.dark());
glEnable(TI_GL_DEPTH_TEST);
//glEnable(GL_CULL_FACE);
//glShadeModel(GL_SMOOTH);
//glEnable(GL_LIGHTING);
//glEnable(GL_LIGHT0);
//glEnable(GL_MULTISAMPLE);
//static GLfloat lightPosition[4] = { 0.5, 5.0, 7.0, 1.0 };
//glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
}
void GLWidget::paintGL()
{
glClear(TI_GL_COLOR_BUFFER_BIT | TI_GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0, 0.0, -6.0);
glRotatef(xRot / 16.0, 1.0, 0.0, 0.0);
glRotatef(yRot / 16.0, 0.0, 1.0, 0.0);
glRotatef(zRot / 16.0, 0.0, 0.0, 1.0);
glDrawELements(model);
}
void GLWidget::resizeGL(int width, int height)
{
//int side = qMin(width, height);
// glViewport((width - side) / 2, (height - side) / 2, side, side);
glViewport(0, 0, width, height);
float sa = 1.f;
if (width * height != 0)
{
sa = float(width) / float(height);
}
glMatrixMode(TI_GL_PROJECTION);
glLoadIdentity();
#ifdef QT_OPENGL_ES_1
glOrthof(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
#else
float halfh = 1.5f;
glOrtho(-halfh*sa, +halfh*sa, -halfh, +halfh, 4.0, 15.0);
#endif
glMatrixMode(TI_GL_MODELVIEW);
}
void GLWidget::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
}
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
int dx = event->x() - lastPos.x();
int dy = event->y() - lastPos.y();
if (event->buttons() & Qt::LeftButton) {
setXRotation(xRot + 8 * dy);
setYRotation(yRot + 8 * dx);
} else if (event->buttons() & Qt::RightButton) {
setXRotation(xRot + 8 * dy);
setZRotation(zRot + 8 * dx);
}
lastPos = event->pos();
}
void GLWidget::updateGL()
{
this->update();
}
void GLWidget::paintEvent(QPaintEvent *event)
{
paintGL();
QPainter p(this);
p.drawImage(0,0,*colorImage);
p.end();
}
void GLWidget::resizeEvent(QResizeEvent *event)
{
int w = event->size().width();
int h = event->size().height();
resizeGL(w,h);
*colorImage = QImage(w,h,QImage::Format_RGB888);
*depthImage = QImage(w,h,QImage::Format_RGB888);
}
main.cpp
// #include "mainwindow.h"
#include <QApplication>
#include <stdio.h>
#include <string>
#include <vector>
#include <iostream>
#include <glwidget.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// MainWindow w;
// w.show();
GLWidget wgt;
wgt.setWindowTitle("test tinyrender");
wgt.show();
return a.exec();
}
文章评论