写一个软光栅器绘制正方体

2021/12/13 6:21:10

本文主要是介绍写一个软光栅器绘制正方体,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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

  

截个图

 



这篇关于写一个软光栅器绘制正方体的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程