这个程序对正方体的绘制时通过软光栅器的方法绘制的,相当于GPU是硬件加速的光栅化,所谓光栅化其实是一种绘画方法,就是在
物体前面放块带栅格的透明玻璃,然后画家在玻璃后面不要动,由于光沿着直线传播,所以从物体上的一小块面片的光射到画家眼睛里面以后
在物体和画家眼睛之间 的栅格玻璃就会投影出一小块面片的相,这块相会占据玻璃上的一些栅格。然后数学上研究,确定物体,栅格,画家眼睛
位置,等一些参数,可以通过几何学精确的计算栅格上哪些格子被占据了,而且哪些格子的颜色,所以都是可以计算的,即计算投影,计算空间变换
计算光照,达到预见真实世界的目的。到电子计算机出现,计算过程被加速了,但是用到的几何学还是几百年前的,现在图片代替了栅格玻璃,像素
即为玻璃上的格子,物体需要先三角化为三角形,这样硬件只需要对一种简单面片实现加速,更多的加速时通过单指令多数据方式实现多个三角面片
同时绘制。
顶点处理阶段,每个点上要预先计算点的法向量,将确定点在光照下的颜色,越和眼睛观察的方向接近的颜色强度越大。
当三角形被确定为图片上的像素后,同时重心插值就开始计算,确定了三角形内某个像素在三角形中的重心坐标,这个归一化的坐标将确定
这个像素的最终颜色,当然还有很多其它用处。
接着这个重心坐标也能确定它离眼睛的距离,经由Z缓冲进入深度剔除阶段,这是实现物体不可见部分上的像素被丢掉的方法。
终于,这个像素可见,颜色已计算出,可以写入图片它应有的格子位置了。
这个程序是用熟悉的Qt的图片读写及显示功能写的,程序主要是备份到博客,实际VC打开全局优化下,动画还很流畅,模仿的nehe的box的例子
3d math是用的osg的vec类,这个osg不用编译,因为vec类很简单,头文件粘贴来都能用,当然更多的OpenGL特性并没有实现,毕竟是研究分享的目的
,软光栅软CPU一般是硬件仿真模拟目的。
tracepix.h trace pixel意思就是描像素,感觉光栅化这个术语很神秘,不知道是不是直译的。整个程序就是一堆计算,最后实际动作就是在QImge上setPixel
#ifndef TRACEPIX_H #define TRACEPIX_H #include <QPainter> #include <osg/Vec2i> #include <osg/Vec3> #include <osg/Vec4> class TracePix { public: enum ElementType { ET_POINTS = 1, ET_LINES, ET_TRIANGLES }; struct Transform { Transform() { center.set(0, 0, 0); xaxis.set(1.f, 0, 0); yaxis.set(0, 1.f, 0); yaxis.set(0, 0, 1.f); } osg::Vec3 center; osg::Vec3 xaxis; osg::Vec3 yaxis; osg::Vec3 zaxis; }; private: std::vector<osg::Vec3> vboPoints; std::vector<osg::Vec3> vboNormals; std::vector<osg::Vec4> vboColors; std::vector<osg::Vec3> vboPointsReg; std::vector<osg::Vec3> vboNormalsReg; std::vector<osg::Vec4> vboColorsReg; std::vector<unsigned int> indices; ElementType elementType; QImage* cbuf; int viewwidth; int viewheight; std::vector<float> zbuf; Transform trans; public: void init(QImage* img) { cbuf = img; viewwidth = cbuf->width(); viewheight = cbuf->height(); zbuf.resize(viewwidth*viewheight,1e4); } const std::vector<osg::Vec3>& getPoints() { return vboPoints; } void setPoints(std::vector<osg::Vec3>& data) { vboPoints = data; vboPointsReg.resize(vboPoints.size()); } const std::vector<osg::Vec3>& getNormals() { return vboNormals; } void setNormals(std::vector<osg::Vec3>& data) { vboNormals = data; vboNormalsReg.resize(vboNormals.size()); } const std::vector<osg::Vec4>& getColors() { return vboColors; } void setColors(std::vector<osg::Vec4>& data) { vboColors = data; vboColorsReg.resize(vboColors.size()); } const std::vector<unsigned int>& getElementsIndices() { return indices; } ElementType getElementType() { return elementType; } void setElementsIndices(ElementType type, std::vector<unsigned int>& data) { elementType = type; indices = data; } void setTransform(osg::Vec3 center, osg::Vec3 xaxis, osg::Vec3 yaxis, osg::Vec3 zaxis) { trans.center = center; trans.xaxis = xaxis; trans.yaxis = yaxis; trans.zaxis = zaxis; } void setTransform(Transform& t) { trans.center = t.center; trans.xaxis = t.xaxis; trans.yaxis = t.yaxis; trans.zaxis = t.zaxis; } Transform getTransform() { return trans; } void drawElements() { if (elementType == ET_POINTS) { drawPoints(); } else if (elementType == ET_LINES) { drawLines(); } else if (elementType == ET_TRIANGLES) { drawTriangles(); } else { return; } } void clear() { cbuf->fill(qRgb(51, 51, 102)); zbuf.assign(viewwidth*viewheight, 1e4); } osg::Vec3 rotateVector(osg::Vec3 vec, osg::Vec3 rotateAxis, float rotateAngleDeg) { float x = rotateAxis.x(); float y = rotateAxis.y(); float z = rotateAxis.z(); float length = sqrt(x * x + y * y + z * z); float inversenorm = 1.0 / length; float coshalfangle = cos(0.5 * rotateAngleDeg); float sinhalfangle = sin(0.5 * rotateAngleDeg); float _v[4]; _v[0] = x * sinhalfangle * inversenorm; _v[1] = y * sinhalfangle * inversenorm; _v[2] = z * sinhalfangle * inversenorm; _v[3] = coshalfangle; osg::Vec3f uv, uuv; osg::Vec3f qvec(_v[0], _v[1], _v[2]); uv = qvec ^ vec; uuv = qvec ^ uv; uv *= (2.0f * _v[3]); uuv *= 2.0f; return vec + uv + uuv; } private: osg::Vec3 transformPoint(osg::Vec3& pt) { return trans.center + trans.xaxis * pt.x() + trans.yaxis * pt.y() + trans.zaxis * pt.z(); } osg::Vec3 transformVec(osg::Vec3& vec) { return trans.xaxis * vec.x() + trans.yaxis * vec.y() + trans.zaxis * vec.z(); } void drawTriangles() { vertex_process(); for (int ti = 0; ti < indices.size(); ti += 3) { int ti0 = indices[ti]; int ti1 = indices[ti+1]; int ti2 = indices[ti+2]; draw_tri(vboPointsReg[ti0], vboPointsReg[ti1], vboPointsReg[ti2], vboNormalsReg[ti0], vboNormalsReg[ti1], vboNormalsReg[ti2], vboColorsReg[ti0], vboColorsReg[ti1], vboColorsReg[ti2]); } } void drawPoints() { } void drawLines() { } void vertex_process() { osg::Vec4 lightColor(1, 1, 0, 1); osg::Vec3 toEyeDir(0, 0, 1); for (int i = 0; i < vboPoints.size(); i++) { vboPointsReg[i] = transformPoint(vboPoints[i]); vboNormalsReg[i] = transformVec(vboNormals[i]); vboColorsReg[i] = vboColors[i]; auto n = vboNormalsReg[i]; float s = fabs(toEyeDir * n); if (s > 1.f) { s = 1.f; } auto& c = vboColorsReg[i]; c = lightColor * s; c.w() = 1.0; } } void draw_tri(osg::Vec3& point_a, osg::Vec3& point_b, osg::Vec3& point_c, osg::Vec3& normal_a, osg::Vec3& normal_b, osg::Vec3& normal_c, osg::Vec4& color_a, osg::Vec4& color_b, osg::Vec4& color_c) { osg::Vec2i a(point_a._v[0], point_a._v[1]); osg::Vec2i b(point_b._v[0], point_b._v[1]); osg::Vec2i c(point_c._v[0], point_c._v[1]); float depth_a = fabs(point_a._v[2]); float depth_b = fabs(point_b._v[2]); float depth_c = fabs(point_c._v[2]); int af_b = (a.y() - b.y()) * (c.x() - b.x()) - (a.x() - b.x()) * (c.y() - b.y()); if (af_b == 0) { return; } int bt_b = (b.y() - c.y()) * (a.x() - c.x()) - (b.x() - c.x()) * (a.y() - c.y()); if (bt_b == 0) { return; } int xmin = 1e6; int xmax = -1e6; int ymin = 1e6; int ymax = -1e6; update_rect(a, xmin, xmax, ymin, ymax); update_rect(b, xmin, xmax, ymin, ymax); update_rect(c, xmin, xmax, ymin, ymax); if (xmax < 0 || xmin >= viewwidth|| ymax < 0 || ymin >= viewheight) { return; } for (int j = ymin; j<=ymax; j++) { for (int i = xmin; i <= xmax; i++) { if (i < 0 || i >= viewwidth || j < 0 || j >= viewheight) { continue; } osg::Vec2i p(i,j); osg::Vec2i ab = b - a; osg::Vec2i bc = c - b; osg::Vec2i ca = a - c; osg::Vec2i ap = p - a; osg::Vec2i bp = p - b; osg::Vec2i cp = p - c; bool iflag = vec2i_cross(ab, ap) >= 0 && vec2i_cross(bc, bp) >= 0 && vec2i_cross(ca, cp) >= 0; if (!iflag) { iflag = vec2i_cross(ab, ap) <= 0 && vec2i_cross(bc, bp) <= 0 && vec2i_cross(ca, cp) <= 0; } if (!iflag) { continue; } int af_t = (p.y() - b.y()) * (c.x() - b.x()) - (p.x() - b.x()) * (c.y() - b.y()); int bt_t = (p.y() - c.y()) * (a.x() - c.x()) - (p.x() - c.x()) * (a.y() - c.y()); float af = float(af_t) / float(af_b); float bt = float(bt_t) / float(bt_b); float ga = 1.f - af - bt; // pixel_process osg::Vec4 color_p = color_a * af + color_b * bt + color_c * ga; float depth_p = depth_a * af + depth_b * bt + depth_c * ga; float depth_dbuf = getDepth(i,j); if (depth_p > depth_dbuf) continue; setDepth(i,j, depth_p); setPixelColor(i,j,color_p); } } } int vec2i_cross(osg::Vec2i& a, osg::Vec2i& b) { return a.x()* b.y() - b.x() * a.y(); } void update_rect(osg::Vec2i& a, int& xmin, int& xmax, int& ymin, int& ymax) { if (a.x() < xmin) { xmin = a.x(); } if (a.x() > xmax) { xmax = a.x(); } if (a.y() < ymin) { ymin = a.y(); } if (a.y() > ymax) { ymax = a.y(); } } void setDepth(int i, int j, float d) { zbuf[viewwidth*j+i] = d; } float getDepth(int i, int j) { return zbuf[viewwidth*j+i]; } void setPixelColor(int i, int j, osg::Vec4 c) { int ir = 255*c.x(); int ig = 255*c.y(); int ib = 255*c.z(); cbuf->setPixel(i, viewheight - 1 - j, qRgb(ir, ig, ib)); } }; #endif // TRACEPIX_H
PixPanel.h
#pragma once #include <QWidget> #include <QTimer> #include "ui_PixPanel.h" class QImage; class TracePix; class PixPanel : public QWidget { Q_OBJECT public: PixPanel(QWidget *parent = Q_NULLPTR); ~PixPanel(); virtual void paintEvent(QPaintEvent* e); private: Ui::PixPanel ui; QImage* mImage; TracePix* mTracePix; QTimer* mTimer; double mXRotAngle; double mYRotAngle; };
PixPanel.cpp
#include "PixPanel.h" #include <QPainter> #include <QImage> #include <iostream> #include "tracepix.h" osg::Vec3 _localToWorld(osg::Vec3& center, osg::Vec3& xaxis, osg::Vec3& yaxis, osg::Vec3& zaxis, osg::Vec3& localVec3) { return center + xaxis * localVec3.x() + yaxis * localVec3.y() + zaxis * localVec3.z(); } void createCubeMeshData(osg::Vec3 center, osg::Vec3 xaxis, osg::Vec3 yaxis, osg::Vec3 zaxis, double m, double n, double h, std::vector<osg::Vec3>& points, std::vector<osg::Vec3>& normals, std::vector<unsigned int>& indices) { // ^z top // | // 7------6 // / | /| // 4------5 | // | 3---|--2 // | / | / ---->x right // 0------1 // / // / // |/-y front std::vector<osg::Vec3> vertices; double halfx = m * 0.5; double halfy = n * 0.5; double halfz = h * 0.5; vertices.push_back(osg::Vec3(-halfx, -halfy, -halfz)); vertices.push_back(osg::Vec3(halfx, -halfy, -halfz)); vertices.push_back(osg::Vec3(halfx, halfy, -halfz)); vertices.push_back(osg::Vec3(-halfx, halfy, -halfz)); vertices.push_back(osg::Vec3(-halfx, -halfy, halfz)); vertices.push_back(osg::Vec3(halfx, -halfy, halfz)); vertices.push_back(osg::Vec3(halfx, halfy, halfz)); vertices.push_back(osg::Vec3(-halfx, halfy, halfz)); for (int i = 0; i < 8; i++) { vertices[i] = _localToWorld(center, xaxis, yaxis, zaxis, vertices[i]); } std::vector<osg::Vec3> plnNormals; plnNormals.push_back(osg::Vec3(0, -1, 0)); //front plnNormals.push_back(osg::Vec3(0, 1, 0)); //back plnNormals.push_back(osg::Vec3(-1, 0, 0)); //left plnNormals.push_back(osg::Vec3(1, 0, 0)); //right plnNormals.push_back(osg::Vec3(0, 0, -1)); //bottom plnNormals.push_back(osg::Vec3(0, 0, 1)); //top osg::Vec3 worldCenter(0, 0, 0); for (int i = 0; i < 6; i++) { plnNormals[i] = _localToWorld(worldCenter, xaxis, yaxis, zaxis, plnNormals[i]); } points.reserve(24); normals.reserve(24); indices.reserve(36); // cube six facets int facets[24] = { 0,1,5,4, 2,3,7,6, 0,4,7,3, 1,2,6,5, 0,3,2,1, 4,5,6,7 }; int normalIdx = 0; int offseti = 0; for (int j = 0; j < 6; j++) { for (int i = 0; i < 4; i++) { points.push_back(vertices[facets[i+offseti]]); normals.push_back(plnNormals[j]); } indices.push_back(0 + offseti); indices.push_back(1 + offseti); indices.push_back(2 + offseti); indices.push_back(0 + offseti); indices.push_back(2 + offseti); indices.push_back(3 + offseti); offseti += 4; } } void createTwoTriangles(TracePix* tracePix) { // v4 v1 // // // v2(v5) v0(v3) std::vector<osg::Vec3> pts; pts.push_back(osg::Vec3(80, 20, -150)); pts.push_back(osg::Vec3(60, 60, -200)); pts.push_back(osg::Vec3(20, 20, -150)); pts.push_back(osg::Vec3(80, 20, -150)); pts.push_back(osg::Vec3(20, 56, -50)); pts.push_back(osg::Vec3(20, 20, -150)); osg::Vec3 facet0_n = (pts[1] - pts[0]) ^ (pts[2] - pts[0]); facet0_n.normalize(); osg::Vec3 facet1_n = (pts[4] - pts[3]) ^ (pts[5] - pts[3]); facet1_n.normalize(); std::vector<osg::Vec3> nms; //nms.push_back(osg::Vec3(0, 0, -1)); //nms.push_back(osg::Vec3(0, 0, -1)); //nms.push_back(osg::Vec3(0, 0, -1)); //nms.push_back(osg::Vec3(0, 0, -1)); nms.push_back(facet0_n); nms.push_back(facet0_n); nms.push_back(facet0_n); nms.push_back(facet1_n); nms.push_back(facet1_n); nms.push_back(facet1_n); std::vector<osg::Vec4> cos; cos.push_back(osg::Vec4(0, 0, 1, 1)); cos.push_back(osg::Vec4(0, 1, 0, 1)); cos.push_back(osg::Vec4(1, 0, 0, 1)); cos.push_back(osg::Vec4(0, 0, 1, 1)); cos.push_back(osg::Vec4(0, 1, 0, 1)); cos.push_back(osg::Vec4(1, 0, 0, 1)); std::vector<unsigned int> idx; idx.push_back(0); idx.push_back(1); idx.push_back(2); idx.push_back(3); idx.push_back(4); idx.push_back(5); tracePix->setPoints(pts); tracePix->setNormals(nms); tracePix->setColors(cos); tracePix->setElementsIndices(TracePix::ET_TRIANGLES, idx); } void createACube(TracePix* tracePix, osg::Vec3 origin, double xsize, double ysize, double zsize, float xRotAngle = 32.f, float yRotAngle = 12.f) { std::vector<osg::Vec3> pts; std::vector<osg::Vec3> nms; std::vector<osg::Vec4> cols; std::vector<unsigned int> idx; osg::Vec3 wc(0, 0, 0); osg::Vec3 c = origin; osg::Vec3 xa(1, 0, 0); osg::Vec3 ya(0, 1, 0); osg::Vec3 za(0, 0, 1); osg::Vec3 tya = tracePix->rotateVector(ya, xa, osg::DegreesToRadians(xRotAngle)); osg::Vec3 tza = tracePix->rotateVector(za, xa, osg::DegreesToRadians(xRotAngle)); osg::Vec3 txa = tracePix->rotateVector(xa, osg::Vec3(0,1,0), osg::DegreesToRadians(yRotAngle)); tya = tracePix->rotateVector(tya, osg::Vec3(0, 1, 0), osg::DegreesToRadians(yRotAngle)); tza = tracePix->rotateVector(tza, osg::Vec3(0, 1, 0), osg::DegreesToRadians(yRotAngle)); tracePix->setTransform(c, txa, tya, tza); createCubeMeshData(wc, xa, ya, za, xsize, ysize, zsize, pts, nms, idx); cols.resize(pts.size(), osg::Vec4(0, 1, 0, 1)); tracePix->setPoints(pts); tracePix->setNormals(nms); tracePix->setColors(cols); tracePix->setElementsIndices(TracePix::ET_TRIANGLES, idx); } PixPanel::PixPanel(QWidget *parent) : QWidget(parent) { ui.setupUi(this); mImage = new QImage(640, 480, QImage::Format_ARGB32); mTracePix = new TracePix(); mTracePix->init(mImage); // createTwoTriangles(mTracePix); mXRotAngle = 32.f; mYRotAngle = 12.f; createACube(mTracePix, osg::Vec3(320,240,-300), 200,200,200, mXRotAngle, mYRotAngle); mTimer = new QTimer(); mTimer->setInterval(60); connect(mTimer, SIGNAL(timeout()), this, SLOT(update())); mTimer->start(); } PixPanel::~PixPanel() { delete mImage; delete mTracePix; } void PixPanel::paintEvent(QPaintEvent* e) { mXRotAngle += 2.0; if (mXRotAngle > 360.0) { mXRotAngle -= 360.0; } mYRotAngle += 3.0; if (mYRotAngle > 360.0) { mYRotAngle -= 360.0; } osg::Vec3 xa(1, 0, 0); osg::Vec3 ya(0, 1, 0); osg::Vec3 za(0, 0, 1); osg::Vec3 tya = mTracePix->rotateVector(ya, xa, osg::DegreesToRadians(mXRotAngle)); osg::Vec3 tza = mTracePix->rotateVector(za, xa, osg::DegreesToRadians(mXRotAngle)); osg::Vec3 txa = mTracePix->rotateVector(xa, osg::Vec3(0, 1, 0), osg::DegreesToRadians(mYRotAngle)); tya = mTracePix->rotateVector(tya, osg::Vec3(0, 1, 0), osg::DegreesToRadians(mYRotAngle)); tza = mTracePix->rotateVector(tza, osg::Vec3(0, 1, 0), osg::DegreesToRadians(mYRotAngle)); TracePix::Transform trans = mTracePix->getTransform(); trans.xaxis = txa; trans.yaxis = tya; trans.zaxis = tza; mTracePix->setTransform(trans); mTracePix->clear(); mTracePix->drawElements(); QPainter p(this); p.drawImage(20, 20, *mImage); p.end(); //static int s_counter = 0; //std::cout << "update " << s_counter << std::endl; //s_counter++; }
PixPanel.ui
<UI version="4.0" > <class>PixPanel</class> <widget class="QWidget" name="PixPanel" > <property name="objectName" > <string notr="true">PixPanel</string> </property> <property name="geometry" > <rect> <x>0</x> <y>0</y> <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle" > <string>PixPanel</string> </property> </widget> <layoutDefault spacing="6" margin="11" /> <pixmapfunction></pixmapfunction> <resources/> <connections/> </UI>
截个图