GAMES101作业2-代码框架逐行理解(c++基础巩固)

目录

补充作业代码

main.cpp

get_projection_matrix函数

rasterize.cpp

1.insideTriangle函数——判断点(x,y)是否在三角形内

2.rasterizer_triangle

结果展示

正三角-n和f取负值

 倒三角-n和f取正值

 代码框架详细理解

Triangle.hpp

rasterizer.hpp

rasterizer.cpp

main.cpp

Triangle.cpp

代码涉及到的c++知识回顾

 宏定义

无参宏定义

 带参宏定义

#ifndef条件编译

#pragma once 

using namespace

using

namespace 命名空间

class 类

struct

enum class枚举类型

等价于enum struct

底层数据类型(underlying tyue)

头文件

inline

先来说说什么是栈空间

inline具体如何实现目的

类外定义前加上inline关键字

operator

为什么要用这个

在类里实现运算符-成员函数

类外的非类的成员函数-全局函数

逻辑运算符&&和||与&和|的区别

empalce_front/emplace/emplace_back


提供的代码框架里包含了三个头文件和三个源文件

 

补充作业代码

需要补充的部分有:

main.cpp

get_projection_matrix函数

注意,与作业1情况类似,如果n和f直接取zNear和zFar则两个三角形会是倒三角。

Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
    // TODO: Copy-paste your implementation from the previous assignment.
    Eigen::Matrix4f projection;
    float f, n, l, r, b, t, fov;
    fov = eye_fov / 180 * MY_PI;
    n = -zNear; //znear是正值
    f = -zFar;
    t = tan(fov / 2) * zNear;
    b = -t;
    r = t * aspect_ratio;
    l = -r;
    //透视->正交 perspective->orthographic
    Eigen::Matrix4f pertoorth;
    pertoorth << n, 0, 0, 0,
        0, n, 0, 0,
        0, 0, n + f, -n * f,
        0, 0, 1, 0;
    //正交——移动
    Eigen::Matrix4f orth1;
    orth1 <<
        1, 0, 0, -(r + l) / 2,
        0, 1, 0, -(t + b) / 2,
        0, 0, 1, -(n + f) / 2,
        0, 0, 0, 1;
    //正交——缩放
    Eigen::Matrix4f orth2;
    orth2 <<
        2 / (r - l), 0, 0, 0,
        0, 2 / (t - b), 0, 0,
        0, 0, 2 / (zFar - zNear), 0,
        0, 0, 0, 1;
    projection = orth2 * orth1 * pertoorth;//注意矩阵顺序,变换从右往左依次进行
    return projection;
}

rasterize.cpp

1.insideTriangle函数——判断点(x,y)是否在三角形内

这个部分重点是在abc三个取值的问题,注意是:x1y2-x2y1

static bool insideTriangle(int x, int y, const Vector3f* _v)//第三个数是_v指向Vector3f类型的指针,默认传入了三角形的三个顶点
{   
    //.head(2)用来取出每个顶点的前两个值(x,y)
    Eigen::Vector2f AB, BC, CA, AP, BP, CP, p;
    float a, b, c;
    p << x, y;
    AB = _v[1].head(2) - _v[0].head(2);
    AP = p - _v[0].head(2);
    BC = _v[2].head(2) - _v[1].head(2);
    BP = p - _v[1].head(2);
    CA = _v[0].head(2) - _v[2].head(2);
    CP = p - _v[2].head(2);
    a = AB[0] * AP[1] - AB[1] * AP[0];
    b = BC[0] * BP[1] - BC[1] * BP[0];
    c = CA[0] * CP[1] - CA[1] * CP[0];
    if (a > 0 && b > 0 && c > 0) return true;
    else if (a < 0 && b < 0 && c < 0) return true;
    return false;
}

下面这个是在作业3里老师给的框架里的方法,比我的简单多了:

static bool insideTriangle(int x, int y, const Vector4f* _v){
    Vector3f v[3];
    for(int i=0;i<3;i++)
        v[i] = {_v[i].x(),_v[i].y(), 1.0};
    Vector3f f0,f1,f2;
    f0 = v[1].cross(v[0]);
    f1 = v[2].cross(v[1]);
    f2 = v[0].cross(v[2]);
    Vector3f p(x,y,1.);
    if((p.dot(f0)*f0.dot(v[2])>0) && (p.dot(f1)*f1.dot(v[0])>0) && (p.dot(f2)*f2.dot(v[1])>0))
        return true;
    return false;
}

2.rasterizer_triangle

这个部分一个取bounding box问题,一个是重心坐标的问题。

void rst::rasterizer::rasterize_triangle(const Triangle& t) { //这里的Triangle是Triangle.hpp里定义的一个class类
    auto v = t.toVector4(); //toVector4()是Triangle里定义的一个array<vector4f,3>
    
    //找到三角形的bounding box
    //在Triangle类中定义了三角形顶点为v[0]v[1]v[2] 都是vector4f
    int min_x = std::min(std::min(v[0].x(), v[1].x()), v[2].x());
    int min_y = std::min(std::min(v[0].y(), v[1].y()), v[2].y());
    int max_x = std::max(std::max(v[0].x(), v[1].x()), v[2].x());
    int max_y = std::max(std::max(v[0].y(), v[1].y()), v[2].y());
    
    //遍历pixels确定像素是否在三角形内

    /*bool MSAA = true;
    这里用MSAA 4X 也就是把像素分成4X4,默认像素是1X1的大小,因此有了0.25、0.75
    std::vector<Eigen::Vector2f>pixel
    {
        {0.25,0.25},{0.25,0.75},{0.75,0.25},{0.75,0.75},
    };
    */
    //循环判断
    for (int x = min_x; x <= max_x; x++) {
        for (int y = min_y; y <= max_y; y++) {
            //判断是否在三角形内
            if (insideTriangle(x + 0.5, y + 0.5, t.v)){
                //在三角形内,用下述code取得插值的z值
                auto[alpha,beta,gamma]=computeBarycentric2D(x + 0.5, y + 0.5, t.v);//为了获得该点的z值
                float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                z_interpolated *= w_reciprocal;

                int cur_index = get_index(x, y);
                if(z_interpolated<depth_buf[cur_index])
                {
                    depth_buf[cur_index] = z_interpolated;
                    Vector3f vertex;
                    vertex << x, y, z_interpolated;
                    set_pixel(vertex, t.getColor());
                }
            }
        }
    }
}

结果展示

正三角-n和f取负值

 倒三角-n和f取正值

 代码框架详细理解

做作业不局限于把框架填完整,要把整个框架理解清楚。

Triangle.hpp

//预处理-宏定义
#ifndef RASTERIZER_TRIANGLE_H //条件编译,判断是否被定义过
#define RASTERIZER_TRIANGLE_H //若没有被定义,则进行宏定义

#include <Eigen> //预处理-文件包含命令


using namespace Eigen; //使用命名空间Eigen
class Triangle{//定义了一个名为Triangle的
public:
    Vector3f v[3]; //按照逆时针顺序定义三角形三个顶点坐标

    //定义三角形每个顶点的属性
    Vector3f color[3]; //每个顶点的颜色
    Vector2f tex_coords[3]; //顶点的(u,v)坐标
    Vector3f normal[3]; //每个顶点的法线

    //Texture *tex;
    Triangle();

    void setVertex(int ind, Vector3f ver); /*set i-th vertex coordinates */
    void setNormal(int ind, Vector3f n); /*set i-th vertex normal vector*/
    void setColor(int ind, float r, float g, float b); /*set i-th vertex color*/
    Vector3f getColor() const { return color[0]*255; } // Only one color per triangle.
    void setTexCoord(int ind, float s, float t); /*set i-th vertex texture coordinate*/
    std::array<Vector4f, 3> toVector4() const;//std::array是用来封装固定大小数组的容器,大小必须在编译期确定
};

#endif //RASTERIZER_TRIANGLE_H 

rasterizer.hpp

#pragma once //防止重复引用,与#ifndef作用相同

#include <Eigen> //引入Eigen库
#include <algorithm> //<algrithm>定义了C++STL标准中的基础性算法,均为函数模板
#include "global.hpp"
#include "Triangle.hpp"
using namespace Eigen; //使用Eigen命名空间,之后就不用写Eigen::了

namespace rst //创建一个命名空间rst
{
    enum class Buffers //定义枚举类Buffers
    {
        Color = 1,
        Depth = 2
    };

    inline Buffers operator|(Buffers a, Buffers b) //operator| 重载非短路或运算
    {
        return Buffers((int)a | (int)b); 
    }

    inline Buffers operator&(Buffers a, Buffers b) //operator& 重载非短路与运算
    {
        return Buffers((int)a & (int)b);
    }

    enum class Primitive //同样,定义一个枚举类primitive
    {
        Line,
        Triangle
    };

    //为确保安全:draw function用了两个buffer id进行讨论,防止用户搞错前后顺序,如果顺序错了编译器就不会进行这段程序。

    //声明三个结构体,sturct默认公有继承,默认是public的
    struct pos_buf_id //position位置
    {
        int pos_id = 0;
    };
    
    struct ind_buf_id //
    {
        int ind_id = 0;
    };

    struct col_buf_id //颜色
    {
        int col_id = 0;
    };

    class rasterizer //声明一个类
    {
    public: //公开部分
        rasterizer(int w, int h);
        pos_buf_id load_positions(const std::vector<Eigen::Vector3f>& positions);
        ind_buf_id load_indices(const std::vector<Eigen::Vector3i>& indices);
        col_buf_id load_colors(const std::vector<Eigen::Vector3f>& colors);

        void set_model(const Eigen::Matrix4f& m);
        void set_view(const Eigen::Matrix4f& v);
        void set_projection(const Eigen::Matrix4f& p);

        void set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color);

        void clear(Buffers buff);

        void draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, col_buf_id col_buffer, Primitive type);

        std::vector<Eigen::Vector3f>& frame_buffer() { return frame_buf; }

    private:
        void draw_line(Eigen::Vector3f begin, Eigen::Vector3f end);

        void rasterize_triangle(const Triangle& t);

        // VERTEX SHADER -> MVP -> Clipping -> /.W -> VIEWPORT -> DRAWLINE/DRAWTRI -> FRAGSHADER

    private:
        Eigen::Matrix4f model;
        Eigen::Matrix4f view;
        Eigen::Matrix4f projection;

        std::map<int, std::vector<Eigen::Vector3f>> pos_buf;
        std::map<int, std::vector<Eigen::Vector3i>> ind_buf;
        std::map<int, std::vector<Eigen::Vector3f>> col_buf;

        std::vector<Eigen::Vector3f> frame_buf;

        std::vector<float> depth_buf;
        int get_index(int x, int y);

        int width, height;

        int next_id = 0;
        int get_next_id() { return next_id++; }
    };
}

rasterizer.cpp

#include <algorithm> //是C++标准算法库,应用在容器上
#include <vector> //是c++标准库提供的一个被封装的动态数组
#include "rasterizer.hpp"
#include <opencv2/opencv.hpp>
#include <math.h>

/*这里的rst是rasterizer.hpp头文件中定义的一个rst namespace
  pos_buf_id 是rst中定义的一个 struct
  rasterizer 是rst中定义的一个 class
*/
rst::pos_buf_id rst::rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions)
{
    auto id = get_next_id();//rasterizer类中定义的一个int类型的函数,作用:return下一个id
    pos_buf.emplace(id, positions);//在id指向的元素前创建一个值为positions的元素

    return {id};
}

rst::ind_buf_id rst::rasterizer::load_indices(const std::vector<Eigen::Vector3i> &indices)
{
    auto id = get_next_id();
    ind_buf.emplace(id, indices);

    return {id};
}

rst::col_buf_id rst::rasterizer::load_colors(const std::vector<Eigen::Vector3f> &cols)
{
    auto id = get_next_id();
    col_buf.emplace(id, cols);

    return {id};
}

auto to_vec4(const Eigen::Vector3f& v3, float w = 1.0f)
{
    return Vector4f(v3.x(), v3.y(), v3.z(), w);
}


static bool insideTriangle(int x, int y, const Vector3f* _v)//第三个数是_v指向Vector3f类型的指针,默认传入了三角形的三个顶点
{   
    //.head(2)用来取出每个顶点的前两个值(x,y)
    Eigen::Vector2f AB, BC, CA, AP, BP, CP, p;
    float a, b, c;
    p << x, y;
    AB = _v[1].head(2) - _v[0].head(2);
    AP = p - _v[0].head(2);
    BC = _v[2].head(2) - _v[1].head(2);
    BP = p - _v[1].head(2);
    CA = _v[0].head(2) - _v[2].head(2);
    CP = p - _v[2].head(2);
    a = AB[0] * AP[1] - AB[1] * AP[0];
    b = BC[0] * BP[1] - BC[1] * BP[0];
    c = CA[0] * CP[1] - CA[1] * CP[0];
    if (a > 0 && b > 0 && c > 0) return true;
    else if (a < 0 && b < 0 && c < 0) return true;
    return false;
}

static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector3f* v)
{
    float c1 = (x*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*y + v[1].x()*v[2].y() - v[2].x()*v[1].y()) / (v[0].x()*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*v[0].y() + v[1].x()*v[2].y() - v[2].x()*v[1].y());
    float c2 = (x*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*y + v[2].x()*v[0].y() - v[0].x()*v[2].y()) / (v[1].x()*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*v[1].y() + v[2].x()*v[0].y() - v[0].x()*v[2].y());
    float c3 = (x*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*y + v[0].x()*v[1].y() - v[1].x()*v[0].y()) / (v[2].x()*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*v[2].y() + v[0].x()*v[1].y() - v[1].x()*v[0].y());
    return {c1,c2,c3};
}

void rst::rasterizer::draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, col_buf_id col_buffer, Primitive type)
{
    auto& buf = pos_buf[pos_buffer.pos_id];
    auto& ind = ind_buf[ind_buffer.ind_id];
    auto& col = col_buf[col_buffer.col_id];

    float f1 = (50 - 0.1) / 2.0;
    float f2 = (50 + 0.1) / 2.0;

    Eigen::Matrix4f mvp = projection * view * model;
    for (auto& i : ind)//用i遍历ind
    {
        Triangle t;
        Eigen::Vector4f v[] = {
                mvp * to_vec4(buf[i[0]], 1.0f),
                mvp * to_vec4(buf[i[1]], 1.0f),
                mvp * to_vec4(buf[i[2]], 1.0f)
        };
        //Homogeneous division
        for (auto& vec : v) {
            vec /= vec.w();
        }
        //Viewport transformation
        for (auto & vert : v)
        {
            vert.x() = 0.5*width*(vert.x()+1.0);
            vert.y() = 0.5*height*(vert.y()+1.0);
            vert.z() = vert.z() * f1 + f2;
        }

        for (int i = 0; i < 3; ++i)
        {
            t.setVertex(i, v[i].head<3>());
            t.setVertex(i, v[i].head<3>());
            t.setVertex(i, v[i].head<3>());
        }

        auto col_x = col[i[0]];
        auto col_y = col[i[1]];
        auto col_z = col[i[2]];

        t.setColor(0, col_x[0], col_x[1], col_x[2]);
        t.setColor(1, col_y[0], col_y[1], col_y[2]);
        t.setColor(2, col_z[0], col_z[1], col_z[2]);

        rasterize_triangle(t);
    }
}

//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) { //这里的Triangle是Triangle.hpp里定义的一个class类
    auto v = t.toVector4(); //toVector4()是Triangle里定义的一个array<vector4f,3>
    
    //找到三角形的bounding box
    //在Triangle类中定义了三角形顶点为v[0]v[1]v[2] 都是vector4f
    int min_x = std::min(std::min(v[0].x(), v[1].x()), v[2].x());
    int min_y = std::min(std::min(v[0].y(), v[1].y()), v[2].y());
    int max_x = std::max(std::max(v[0].x(), v[1].x()), v[2].x());
    int max_y = std::max(std::max(v[0].y(), v[1].y()), v[2].y());
    
    //遍历pixels确定像素是否在三角形内

    //循环判断
    for (int x = min_x; x <= max_x; x++) {
        for (int y = min_y; y <= max_y; y++) {
            //判断是否在三角形内
            if (insideTriangle(x + 0.5, y + 0.5, t.v)){
                //在三角形内,用下述code取得插值的z值
                auto[alpha,beta,gamma]=computeBarycentric2D(x + 0.5, y + 0.5, t.v);//为了获得该点的z值
                float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                z_interpolated *= w_reciprocal;

                int cur_index = get_index(x, y);
                if(z_interpolated<depth_buf[cur_index])
                {
                    depth_buf[cur_index] = z_interpolated;
                    Vector3f vertex;
                    vertex << x, y, z_interpolated;
                    set_pixel(vertex, t.getColor());
                }
            }
        }
    }
}

void rst::rasterizer::set_model(const Eigen::Matrix4f& m)
{
    model = m;
}

void rst::rasterizer::set_view(const Eigen::Matrix4f& v)
{
    view = v;
}

void rst::rasterizer::set_projection(const Eigen::Matrix4f& p)
{
    projection = p;
}

void rst::rasterizer::clear(rst::Buffers buff)
{
    if ((buff & rst::Buffers::Color) == rst::Buffers::Color)
    {
        std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0});
    }
    if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth)
    {
        std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity());
    }
}

rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
    frame_buf.resize(w * h);
    depth_buf.resize(w * h);
}

int rst::rasterizer::get_index(int x, int y)
{
    return (height-1-y)*width + x;
}

void rst::rasterizer::set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color)
{
    //old index: auto ind = point.y() + point.x() * width;
    auto ind = (height-1-point.y())*width + point.x();
    frame_buf[ind] = color;

}

// clang-format on

main.cpp

main其实跟作业1的差不多,这里就不粘贴解释了。

Triangle.cpp

#include "Triangle.hpp"
#include <algorithm>
#include <array>


Triangle::Triangle() {//Triangle头文件中定义了类Triangle,因此可以使用域操作符::表示Triangle()是类Triangle的成员函数
    v[0] << 0,0,0;
    v[1] << 0,0,0;
    v[2] << 0,0,0;

    color[0] << 0.0, 0.0, 0.0;
    color[1] << 0.0, 0.0, 0.0;
    color[2] << 0.0, 0.0, 0.0;

    tex_coords[0] << 0.0, 0.0;
    tex_coords[1] << 0.0, 0.0;
    tex_coords[2] << 0.0, 0.0;
}

void Triangle::setVertex(int ind, Vector3f ver){
    v[ind] = ver;
}
void Triangle::setNormal(int ind, Vector3f n){
    normal[ind] = n;
}
void Triangle::setColor(int ind, float r, float g, float b) {
    if((r<0.0) || (r>255.) ||
       (g<0.0) || (g>255.) ||
       (b<0.0) || (b>255.)) {
        fprintf(stderr, "ERROR! Invalid color values");
        fflush(stderr);
        exit(-1);
    }

    color[ind] = Vector3f((float)r/255.,(float)g/255.,(float)b/255.);
    return;
}
void Triangle::setTexCoord(int ind, float s, float t) {
    tex_coords[ind] = Vector2f(s,t);
}

std::array<Vector4f, 3> Triangle::toVector4() const
{
    std::array<Eigen::Vector4f, 3> res;
    std::transform(std::begin(v), std::end(v), res.begin(), [](auto& vec) { return Eigen::Vector4f(vec.x(), vec.y(), vec.z(), 1.f); });
    return res;//返回的是一个数组,3个参数,每个参数:
}

代码涉及到的c++知识回顾

 宏定义

参考:宏定义#define #ifndef #endif_马小超i的博客-CSDN博客

#define #ifndef #endif  宏”分为有参数和无参数两种。其中#表示这是一条预处理命令,凡是#开头的都是预处理命令,预处理命令分为三种:宏定义、文件包含、条件编译

无参宏定义

一般表示为#define 标识符 字符串

“define为宏定义命令;“标识符”是定义的宏名;“字符串”可以是常数、表达式、格式串等,要终止其作用域可以使用#undef。

#include<iostream>
#define M (a+b)
using namespace std;
int main() {
	int sum, a, b;
	cout << "input a b:";
	cin >> a >> b;
	sum = M * M;
	cout << sum << endl;
	return 0;
}

 带参宏定义

#define 宏名(形参表) 字符串

调用的时候要用实参去替换形参。

#include<iostream>
#define plus(x) ++x
using namespace std;
int main() {
	int a;
	cout << "input a:";
	cin >> a;
	cout << plus(a) << endl;
	return 0;
}

#ifndef条件编译

#表示预处理命令;ifndef是if not define的缩写,是与处理功能的条件编译,目的是防止头文件的重复包含和编译。

在c++中用#ifndef可以避免出现以下错误:在.hpp头文件中定义了全局变量,一个.cpp文件中多次包含这个.hpp文件,加上#ifndef可以避免重复定义的错误。

#ifndef x //判断x是否已经被宏定义
#define x
    程序段1 //如果x没被定义,定义x,并编译程序段1
#endif
	  程序段2 //如果x已经被定义,忽略1编译2

#pragma once 

除了用#ifndef防止h文件的代码重复引用,还可以用#pragma once指令,在想要保护的文件开头写入

#pragma once

 相比#ifndef(灵活,兼容性好),#pragma once编译时间相对较短,操作简单相率高;是一个非标准但被广泛支持的方式。

using namespace

using

C++11以后,using也可以用来像typedef一样定义类型别名,可读性更强,可以像定义一个变量一样给一个类型定义别名。

namespace 命名空间

namespace+命名空间的名字

相当于一个文件夹,把需要的变量、对象、函数等包含进去,后续可以直接rst::或using namespace 来使用。每个命名空间都有自己的作用域。

同时C++11新标准引入了内联命名空间(在namespace前加上inline)可以直接被外层的命名空间直接使用。

namespace myName{
//相关定义及声明
}

如何访问命名空间?

(1)myName:: 标识符

日常写代码用到的cout/cin/endl等等都是C++的标识符

#include <iostream>
int main()
{
std::cout<<"hello world!"<<std::endl;
}

(2)using namespace myName;

在头文件后都会加上using namespace std;这样可以直接用cout等标识符了,很方便。

#include <iostream>
using namespace std;
int main()
{
cout<<"hello world!"<<endl;
}

class 类

class声明类似struct,基本结构是:

class 类名
{
    public:
    //行为或属性
    private:
    //行为或属性
};

其中:public/private为限制行为/属性的关键字,public表示内容是公开的,可以被外界(例如main等)公开访问也可以被class类自己访问;private则表示内容是私密的,只能在内部调用,不能外部访问。结束末尾有分号;

class Triangle{

public:
    Vector3f v[3]; 
    
    Vector3f color[3]; 
    Vector2f tex_coords[3]; 
    Vector3f normal[3]; 

    Triangle();

    void setVertex(int ind, Vector3f ver); 
    void setNormal(int ind, Vector3f n); 
    void setColor(int ind, float r, float g, float b); 
    Vector3f getColor() const { return color[0]*255; } 
    void setTexCoord(int ind, float s, float t); 
    std::array<Vector4f, 3> toVector4() const;
};

定义了一个名为Triangle的类,定义了setVertex()/setNormal()等成员函数,在类外对其进行定义时函数头就应该是 void Triangle::setVertex()这种形式,其返回类型、成员函数名、参数列表都应该与类内声明的形式一致。

例如在代码框架的Triangle.cpp文件中,就有如下:

Triangle::Triangle() {
    ...
}

void Triangle::setVertex(int ind, Vector3f ver){
    v[ind] = ver;
}

struct

struct结构体,默认公有继承,是public的。C++中的struct不同于C中的,C中的struct不能定义函数只能定义数据成员,数据和操作是分开的;C++中的struct可以包含成员函数,可修改public / private / protected,可以直接用{}初始化。

struct test
{
    int a;
    int b;
};
//完全可以用大括号对数据进行初始化
test A={1,2};

但是,C++有class类了,可以实现struct的功能。

enum class枚举类型

等价于enum struct

旧版本是enum,但有些问题,因此使用enum class/enum struct具有更好的类型安全和类似封装的特性(scoped nature)。参数仅在color作用域内生效。

enum class color{red, green, yellow};
enum class colorx{red, green = 100, yellow};
...

底层数据类型(underlying tyue)

默认为int,可以通过:type:类型 指定任何整形作为底层数据类型。

enum class color: long long {yellow, black};

访问需要通过域运算符访问,不可以直接通过访问枚举成员名称来访问,因此名称重复不会造成影响。如下:

#include<iostream>
using namespace std;

enum class color { red, green };
enum class colorx { red=1, green };

int main()
{
    //这里用了static_cast<int>强转类型为整型int
	cout << static_cast<int>(color::red) << endl;
	cout << static_cast<int>(colorx::red) << endl;
	return 0;
}

 

<algorithm>头文件

定义了C++ STL标准中的基础性的算法(均为函数模板)。<algorithm>定义了设计用于元素范围的函数集合。具体有什么函数模板可参考:C++/C++11中头文件algorithm的使用

inline

参考:inline的使用

先来说说什么是栈空间

栈空间也叫栈内存,可以理解成程序放置局部数据的内存空间。栈空间是有限的,频繁大量的使用会导致程序出错,例如某函数的死循环的尽头就是将栈内存耗尽。

为了解决一些频繁调用的小函数大量消耗栈内存的问题,引入了inline修饰符,表示为内联函数。

inline具体如何实现目的

#include<iostream>
using namespace std;

inline string test(int a)
{
	return (a % 2 > 0) ? "奇数" : "偶数";
}

int main()
{
	int i = 0;
	for (i = 1; i < 100; i++)
	{
		cout << test(i) << endl;
	}
	return 0;
}

上述例子就是把调用到test(i)的地方都换成了 (a % 2 > 0) ? "奇数" : "偶数",避免频繁调用函数。但是:inline的使用对编译器来说只是一个建议,编译器可以选择忽略这个建议,取决于函数的长短;当你将一个1000多行的函数定位inline,编译器还会按照普通函数进行运行。

类外定义前加上inline关键字

例如本题的rasterize.hpp里:下面的部分就是在类外定义。

    ...
    enum class Buffers
    {
        Color = 1,
        Depth = 2
    };

    inline Buffers operator|(Buffers a, Buffers b) 
    {
        return Buffers((int)a | (int)b); //位的或运算
    }
    ...

operator

参考:C++编程语言中重载运算符(operator)介绍

operator是C++的一个关键字,和运算符一起使用(例如:operator+),表示一个运算符重载函数,可将其视为一个函数名。

为什么要用这个

C++提供的运算符只支持基本数据类型和标准库中提供的类操作,对于自己定义的class类,想要实现运算符操作(比大小、判断是否相等)就需要自己定义运算符的实现过程

在类里实现运算符-成员函数

在类中声明需要重载的运算符,方式跟普通的成员函数一样,名称为operator+运算符,例如:

...
class person
{
private:
    int age;
public:
    person(int nage)
    {
        this->age = nage;
    }
    
    bool operator==(const person& ps)
    {
        if(this->age == ps.age)
        {
            return true;
        }
        return false;
    }
};
...

类外的非类的成员函数-全局函数

代表左操作数的参数必须被显式指定。

...
class person
{
public:
    int age;
};
 
bool operator==(person const& p1 ,person const& p2)
{
    if (p1.age == p2.age)
    {
        return true;
    }
    else
    {
        return false;
    }
}
...

逻辑运算符&&和||与&和|的区别

(1)与:&不具有短路效果,左边false右边还会执行;&&有短路效果,左边为false右边不执行;

(2)或:|不具有短路效果,左边为true右边还会执行;||有短路效果,左边为true右边不执行。

empalce_front/emplace/emplace_back

参考:stl之emplace函数的使用

是c++11标准引入的三个新成员,这些操作不是拷贝元素,是在容器中构造函数。因此相比push_back等函数能更好地避免内存的拷贝与移动,使容器插入元素的性能得到进一步提升。

c.emplace_back(t) //在c的尾部创建一个值为t的元素
c.emplace_front(t) //在c的头部创建一个值为t的元素
c.emplace(p,t) //在迭代器p所指向的元素之前创建一个值为t的元素,并返回指定新添加元素的迭代器

  • 21
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九九345

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值