修改tinyrender为部分opengl函数及和glut绘制程序对照

修改了tinyrender的一些地方,tinyrender貌似说的也不清楚,把tinyrender的一些过程和opengl函数对照起来,主要用于光栅器原理阐述,下面粘贴修改的地方。这个软光栅器只是学习目的,没有依赖,用法是将github 的tinyrenderer下载下来 main.cpp tgaimage.h/tgaimage.cpp geometry.h/geometry.cpp model.h/model.cpp our_gl.h/our_gl.cpp 这9个文件对应的安照下面修改的修改,然后VC2015或者2019创建空工程,要C++11以上语法才行,VC2010估计都不支持了,将这9个文件添加到工程中编译就行了,注意文件要UTF-8 BOM编码格式,可以用notepad++改编码格式

model.h 将变量设为public, 用于自己创建model

#ifndef __MODEL_H__
#define __MODEL_H__

#include <vector>
#include <string>
#include "geometry.h"
#include "tgaimage.h"

class Model {
public:
    std::vector<Vec3f> verts_;
    std::vector<std::vector<Vec3i> > faces_; // attention, this Vec3i means vertex/uv/normal
    std::vector<Vec3f> norms_;
    std::vector<Vec2f> uv_;
    TGAImage diffusemap_;
    TGAImage normalmap_;
    TGAImage specularmap_;
    void load_texture(std::string filename, const char *suffix, TGAImage &img);
public:
    Model(){}
    Model(const char *filename);
    ~Model();
    int nverts();
    int nfaces();
    Vec3f normal(int iface, int nthvert);
    Vec3f normal(Vec2i uv);
    Vec3f vert(int i);
    Vec3f vert(int iface, int nthvert);
    Vec2i uv(int iface, int nthvert);
    TGAColor diffuse(Vec2i uv);
    float specular(Vec2i uv);
    std::vector<int> face(int idx);
};

#endif //__MODEL_H__

our_gl类实现几个opengl函数,其中
glutInit初始时设置colorbuffer, depthbuffer, 缺省shader
glDrawElements,是将model作为vbo数据传进去绘制

#ifndef __OUR_GL_H__
#define __OUR_GL_H__

#include "tgaimage.h"
#include "geometry.h"
#include "model.h"

#define TI_GL_CLEAR_COLOR_BUFFER 0x01
#define TI_GL_CLEAR_DEPTH_BUFFER 0x02
#define TI_GL_DEPTH_TEST 0x01

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;
};

namespace our_gl
{
    void glutInit(int w, int h, TGAImage* colorImage, TGAImage* zImage, IShader* defaultShader);
    void glutFinal();
    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);
}

#endif //__OUR_GL_H__

our_gl.cpp

#include <cmath>
#include <limits>
#include "our_gl.h"

Matrix ModelView;
Matrix Viewport;
Matrix Projection;


TGAImage* _colorImage = nullptr;
TGAImage*_zImage = nullptr;
float* _zbufreal = nullptr;
TGAColor clearColor(102,102,204);
TGAColor depthValue(255);
float depthvreal = 255.f;
unsigned int testflag = 0;
IShader* _defaultShader = nullptr;

void triangle(Vec3i *pts, IShader &shader, TGAImage &image, TGAImage &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, TGAImage* colorImage, TGAImage* zImage, IShader* defaultShader)
{

    _zbufreal = new float[w*h];
    _colorImage = colorImage;
    _zImage = zImage;
    _defaultShader = defaultShader;
}

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;
}

void glFrustum(float l, float r, float b, float t, float n, float f)
{
    Projection = Matrix::identity();
    auto& m = Projection;

    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_CLEAR_COLOR_BUFFER)
    {
        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);
            }
        }
    }

    if (flag & TI_GL_CLEAR_DEPTH_BUFFER)
    {
        int w = _zImage->get_width();
        int h = _zImage->get_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;
            }
        }


    }

}

void glEnable(unsigned int flag)
{
    testflag |= flag;
}

void glDisable(unsigned int flag)
{
    testflag &= ~flag;
}

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, TGAImage &image, TGAImage &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;
            }

            // 片元处理
            bool discard = shader.fragment(c, color);
            if (discard) {
                continue;
            }

            float& pre_frag_d_r = zbuf_real[P.y*zbuffer.get_width() + P.x];

            if (fragmentTestFlag& TI_GL_DEPTH_TEST)//是否深度测试
            {
                // zbuffer的该像素位置的深度值,前一个片元的深度
                int pre_fragment_depth = zbuffer.get(P.x,P.y)[0];

                // 如果P的深度比该屏幕位置的前一个片元的深度大
                // 这里z是看的方向
                if (p_depth >=pre_frag_d_r)
                {
                    continue;
                }
            }


            zbuffer.set(P.x, P.y, TGAColor(P.z));
            pre_frag_d_r = p_depth;


            image.set(P.x, P.y, color);

        }
    }
}

tgaimage.h tgaimage.cpp没有改动

main.cpp 很类似glut程序,先构造vbo,这里就是用model读取head.obj模型,也可以自己创建 test_createtriangle就是,调glutInit,传进去colorImage和depthImage两个图片,缺省的Gouraud光照shader
接着gluLookat设置观察方位, glFrustum设置透视参数,然后glDrawElements绘制vbo

main.cpp

#include <vector>
#include <iostream>

#include "tgaimage.h"
#include "model.h"
#include "geometry.h"
#include "our_gl.h"

const int width  = 800;
const int height = 800;

Vec3f light_dir(1,1,1);
Vec3f       eye(0.0,0.0,3);
Vec3f    center(0.0,0.0,0);
Vec3f        up(0,1,0);

TGAImage image(width, height, TGAImage::RGB);
TGAImage zbuffer(width, height, TGAImage::GRAYSCALE);

struct GouraudShader : public IShader {
    Vec3f varying_intensity; // written by vertex shader, read by fragment shader
    Model* modelptr;
    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
        varying_intensity[nthvert] = std::max(0.f, modelptr->normal(iface, nthvert)*light_dir); // get diffuse lighting intensity
        return proj<3>(gl_Vertex/gl_Vertex[3]);                  // project homogenious coordinates to 3d
    }

    virtual bool fragment(Vec3f bar, TGAColor &color) {
        float intensity = varying_intensity*bar;   // interpolate intensity for the current pixel

        color = TGAColor(255, 255, 255)*intensity; // well duh
        return false;                              // no, we do not discard this pixel
    }
};

void test_createtriangle(Model*& model)
{
    model = new Model();
    model->verts_.push_back(Vec3f(-0.1,-0.1,-0.4));
    model->verts_.push_back(Vec3f(0.1,-0.1,-0.4));
    model->verts_.push_back(Vec3f(0.f,0.3,-0.4));
    model->norms_.push_back(Vec3f(0.f,0.f,1.f));
    model->norms_.push_back(Vec3f(0.f,0.f,1.f));
    model->norms_.push_back(Vec3f(0.f,0.f,1.f));
    model->faces_.resize(1);
    model->faces_[0].push_back(Vec3i(0,0,0));
    model->faces_[0].push_back(Vec3i(1,0,0));
    model->faces_[0].push_back(Vec3i(2,0,0));
}

struct BoundBox3f
{
    Vec3f _min;
    Vec3f _max;

    BoundBox3f()
    {
        _min = Vec3f(1e10,1e10,1e10);
        _max = Vec3f(-1e10,-1e10,-1e10);
    }

    Vec3f getCenter()
    {
        return (_min+_max)*0.5f;
    }

    float getRadius()
    {
        Vec3f _v = _max - _min;
        return sqrt(_v[0]*_v[0] + _v[1]*_v[1] +_v[2]*_v[2] )*0.5f;
    }
};

void getModelBoundBox(Model* model, BoundBox3f& box)
{
    auto& boxmin = box._min;
    auto& boxmax = box._max;
    boxmin = model->vert(0,0);
    boxmax = boxmin;
    for (int i=0; i<model->nfaces(); i++)
    {
        for (int j=0; j<3; j++)
        {
            auto v = model->vert(i,j);

            for (int k = 0; k<3; k++)
            {
                if (v[k] > boxmax[k])
                {
                    boxmax[k] = v[k];
                }
                if (v[k] < boxmin[k])
                {
                    boxmin[k] = v[k];
                }
            }

        }
    }
}

using namespace our_gl;

int main(int argc, char** argv)
{
    GouraudShader grdshader;

    glutInit(width,height, &image, &zbuffer, &grdshader);

    //构造vbo model阶段
    Model* model = new Model("d:/models/obj/african_head.obj");
    BoundBox3f model_box;
    getModelBoundBox(model, model_box);
    auto model_center = model_box.getCenter();
    auto model_r = model_box.getRadius();
    grdshader.modelptr = model;

    float rv = model_r*0.66f;
    Vec3f seeto = Vec3f(1.f,0.f,2.f).normalize();
    Vec3f eyePos = Vec3f(0,0,0)+seeto*(model_r*3.5f);
    gluLookAt(eyePos, Vec3f(0.f,0.f,0.f), up);
    glViewport(0, 0, width, height);

    glFrustum(-rv,rv,-rv,rv,model_r*2.f,model_r*4.5);

    light_dir.normalize();

    glClear(TI_GL_CLEAR_COLOR_BUFFER | TI_GL_CLEAR_DEPTH_BUFFER);
    glEnable(TI_GL_DEPTH_TEST);

    // glDrawElements
    glDrawELements(model);

    //
    image.flip_vertically(); // to place the origin in the bottom left corner of the image
    zbuffer.flip_vertically();
    image.write_tga_file("output.tga");
    zbuffer.write_tga_file("zbuffer.tga");

    delete model;

    glutFinal();

    std::cout << "render model done.\n";

    return 0;
}

最后write_tga_file将渲染(绘制)的图片保存到output.tga文件,打开如下
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值