写一个软光栅器绘制正方体
这个程序对正方体的绘制时通过软光栅器的方法绘制的,相当于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>
截个图

浙公网安备 33010602011771号