精华内容
下载资源
问答
  • 光栅化渲染器

    2017-05-16 13:19:53
    简单的光栅化渲染器,实现几何阶段,图元装配,Triangle Setup,光栅阶段
  • c++软件光栅化渲染器


    摘要

    用C++写一个简陋的光栅化渲染器。

    Github地址:Friedsoda/MyRenderer: A tiny renderer in C++



    三角形光栅化

    t
    遍历Bounding box内的每一个像素,判断其是否在三角形内,如果是则绘制这个像素。

    叉乘
    这里利用叉乘的性质,计算P0P1×P0QP_0P_1\times P_0QP1P2×P1QP_1P_2\times P_1QP2P0×P2QP_2P_0\times P_2Q,如果三者同号则P在三条线段的同一边,即处于三角形内部,如果不同号则在三角形外部。



    背面剔除

    对一个读入的模型,逐个三角形计算顶点在屏幕上的坐标,进行光栅化。

    for (int i = 0; i < model -> nfaces(); i++) { 
        std::vector<int> face = model->face(i); 
        Vec2i screen_coords[3]; 
        Vec3f world_coords[3]; 
        for (int j = 0; j < 3; j++) { 
            Vec3f v = model->vert(face[j]); 
            screen_coords[j] = Vec2i((v.x + 1.) * width/2., (v.y + 1.) * height/2.); 
            world_coords[j]  = v; 
        } 
        Vec3f n = (world_coords[2] - world_coords[0]) ^ (world_coords[1] - world_coords[0]); 
        n.normalize(); 
        // dot product < 0 the light comes from behind
        float intensity = n * light_dir; 
        if (intensity > 0) { 
            triangle(screen_coords[0], screen_coords[1], screen_coords[2], image, TGAColor(intensity * 255, intensity * 255, intensity * 255, 255)); 
        } 
    }
    

    计算法线n,用法线和光源方向进行点乘,如果结果为负,说明光从该三角形面的背后照射,即这个三角形面位于背面,不需要进行渲染,所以只取法线大于0的三角形进行光栅化。

    效果:
    a


    深度测试 z-buffer

    Z-Buffer算法为每个像素点维持一个深度数组记为zbuffer,初始值置为无穷大(即离摄像机无穷远),随后遍历每个三角形面上的每一个像素点,如果该像素点的深度值z小于zbuffer中的值,则更新zbuffer值为该点深度值z,并同时更新该像素点颜色为该点颜色。

    添加了z-buffer后的光栅化算法:

    void triangle(Vec3f *pts, float *zbuffer, TGAImage &image, TGAColor color) {
        // bounding box
        Vec2f bboxmin( std::numeric_limits<float>::max(),  std::numeric_limits<float>::max());
        Vec2f bboxmax(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max());
        Vec2f clamp(image.get_width() - 1, image.get_height() - 1);
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 2; j++) {
                bboxmin[j] = std::max(0.f,      std::min(bboxmin[j], pts[i][j]));
                bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j]));
            }
        }
        
        Vec3f P;
        for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) {
            for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) {
                Vec3f bc_screen  = barycentric(pts[0], pts[1], pts[2], P);
                if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0) continue;
                P.z = 0;
                for (int i = 0; i < 3; i++)
                    P.z += pts[i][2] * bc_screen[i];
                // Compare to z-buffer value, if P.z > z-buffer, then draw it
                if (zbuffer[int(P.x + P.y * width)] < P.z) {
                    zbuffer[int(P.x + P.y * width)] = P.z;
                    image.set(P.x, P.y, color);
                }
            }
        }
    }
    

    应用z-buffer的效果:
    在这里插入图片描述

    视图变换与着色器

    之前的模型都是以正交投影展示,为了模拟真实视觉,更多采用透视投影。关于视图变换,之前的博客有讲到,这里就不展开。

    图形学基础 | 视图变换

    添加视图变换功能以及摄像机移动的功能,可以从不同角度渲染模型。首先在render.cpp中定义Ishader类,包含了顶点着色器和片元着色器的功能。

    struct IShader {
        virtual ~IShader();
        virtual Vec4f vertex(int iface, int n) = 0;
        virtual bool fragment(Vec3f bar, TGAColor &color) = 0;
    };
    

    这里用Gouraud Shading方法:用每个面的法线向量插值得到所有点的法线向量。对于每个顶点,将所有共享这个点的面的法线向量相加求均值。接着再用重心坐标插值,得到每一个像素的颜色。

    在顶点着色器中,通过摄像机、投影、视口变换来得到屏幕上的顶点坐标,用法线与光源来计算intensity值,传到片元着色器中,片元着色器通过插值得到所有像素的颜色。片元着色器的返回值是bool(要不要取消绘制这个像素,默认值为false)。

    struct GouraudShader : public IShader {
        Vec3f varying_intensity;
    
        virtual Vec4f vertex(int iface, int n) {
            Vec4f gl_Vertex = embed<4>(model->vert(iface, n)); // read vertex
            gl_Vertex = Viewport * Projection * ModelView * gl_Vertex; // transform
            varying_intensity[n] = std::max(0.f, model->normal(iface, n) * light_dir); // get diffuse lighting intensity
            return gl_Vertex;
        }
    
        virtual bool fragment(Vec3f bar, TGAColor &color) {
            float intensity = varying_intensity * bar; // interpolate intensity
            color = TGAColor(255, 255, 255) * intensity;
            return false;
        }
    };
    

    使用方法是对每一个三角形面,逐个顶点调用顶点着色器,接着再把着色器的输出值(顶点的屏幕坐标、法线、插值系数等信息)传到三角形绘制函数中。

    GouraudShader shader;
    for (int i = 0; i < model->nfaces(); i++) {
        Vec4f screen_coords[3];
        for (int j = 0; j < 3; j++) {
            screen_coords[j] = shader.vertex(i, j);
        }
        triangle(screen_coords, shader, image, zbuffer);
    }
    

    在三角形绘制函数中,通过深度测试后,满足片元着色器条件的点会被绘制出来。

    bool discard = shader.fragment(c, color);
    if (!discard) {
        zbuffer.set(P.x, P.y, TGAColor(frag_depth));
        image.set(P.x, P.y, color);
    }
    

    初步效果:
    在这里插入图片描述
    插值后可以渲染出平滑的模型。
    实现了着色器功能后,就可以开始添加纹理贴图、光照等功能。



    纹理贴图

    通过修改着色器,可以产生不同的效果,例如修改颜色和插值的效果:

    virtual bool fragment(Vec3f bar, TGAColor &color) {
        float intensity = varying_intensity * bar;
        if (intensity > .85) intensity = 1;
        else if (intensity > .60) intensity = .80;
        else if (intensity > .45) intensity = .60;
        else if (intensity > .30) intensity = .45;
        else if (intensity > .15) intensity = .30;
        else intensity = 0;
        color = TGAColor(255, 155, 0) * intensity;
        return false;
    }
    

    在这里插入图片描述

    同样地,纹理使用定义的uv值,插值到每一个像素上。

    struct Shader : public IShader {
        Vec3f varying_intensity;
        mat<2,3,float> varying_uv;   
    
        virtual Vec4f vertex(int iface, int nthvert) {
            varying_uv.set_col(nthvert, model->uv(iface, nthvert));
            varying_intensity[nthvert] = std::max(0.f, model->normal(iface, nthvert)*light_dir); 
            Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); 
            return Viewport * Projection * ModelView * gl_Vertex; 
        }
        
        virtual bool fragment(Vec3f bar, TGAColor &color) {
            float intensity = varying_intensity * bar; 
            Vec2f uv = varying_uv * bar; 
            color = model->diffuse(uv)*intensity;   
            return false;                             
        }
    };
    

    可以贴上纹理了:

    在这里插入图片描述



    法线贴图

    法线贴图把法线向量存储在一张纹理中,然后用贴图的方式来造成视觉上的凹凸效果。可以用切线空间和世界空间两种实现方式,在另一篇博客里有提到切线空间的实现方法:图形学基础 | 纹理映射原理和应用。这里用的是世界空间下的方法:

    struct Shader : public IShader {
        mat<2,3,float> varying_uv;  
        mat<4,4,float> uniform_M;   //  Projection * ModelView
        mat<4,4,float> uniform_MIT; //  (Projection * ModelView).invert_transpose()
    
        virtual Vec4f vertex(int iface, int nthvert) {
            varying_uv.set_col(nthvert, model->uv(iface, nthvert));
            Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); 
            return Viewport * Projection * ModelView * gl_Vertex; 
       }
    
        virtual bool fragment(Vec3f bar, TGAColor &color) {
            Vec2f uv = varying_uv * bar;            
            Vec3f n = proj<3>(uniform_MIT * embed<4>(model->normal(uv))).normalize();
            Vec3f l = proj<3>(uniform_M * embed<4>(light_dir        )).normalize();
            float intensity = std::max(0.f, n * l);
            color = model->diffuse(uv) * intensity;     
            return false;   
        }
    };
    

    这里已经引入了光照模型的概念,使用了法线与光照之间的角度来计算漫反射,效果如下:

    在这里插入图片描述



    高光模型

    光照模型就不再赘述了… 在前一节的基础上加入了高光,片元着色器修改如下:

    virtual bool fragment(Vec3f bar, TGAColor &color) {
        Vec2f uv = varying_uv * bar;
        Vec3f n = proj<3>(uniform_MIT * embed<4>(model->normal(uv))).normalize();
        Vec3f l = proj<3>(uniform_M * embed<4>(light_dir)).normalize();
        Vec3f r = (n * (n * l * 2.f) - l).normalize();
        float spec = pow(std::max(r.z, 0.0f), model->specular(uv));
        float diff = std::max(0.f, n * l);
        TGAColor c = model->diffuse(uv);
        color = c;
        for (int i = 0; i < 3; i++)
            color[i] = std::min<float>(5 + c[i] * (diff + .6 * spec), 255);
        return false;
    }
    

    效果:(总是看这个大哥的头好腻了噜

    在这里插入图片描述



    阴影

    sorry 还没做

    展开全文
  • 软件光栅化渲染器

    2017-12-13 11:03:37
    为了了解光栅化渲染器工作原理及底层实现,准备着手写一个光栅化渲染器。 第一节,我打算先实现所需要的数学库。 Matrix的主要函数主要如下:
    为了了解光栅化渲染器工作原理及底层实现,准备着手写一个光栅化渲染器。
    

    第一节,我打算先实现所需要的数学库。

    mathf.h中主要包含如下数学方法

    float radians(float value);
    float clamp(float value, float min1 = 0, float max1 = 1);
    float lerp(float min, float max, float gradient);
    float smoothStep(float min,float max,float x);


    vectorf.h中包含vector2和vector3两个结构体,主要实现了:

    float Distance(Vector2 &value)const;
    float Dot(const Vector2 &value)const;
    float Magnitude()const;
    Vector2 Normalize()const;
    Vector3 Cross(const Vector3 &value)const;
    float Distance(Vector3 &value)const;
    float Dot(const Vector3 &value)const;
    float Magnitude()const;
    Vector3 Normalize()const;


    并且实现了运算符重载。


    matrix.h中实现了Matrix4x4结构体,内部主要是对运算符进行重载。


    重点来了,rmathf.h中实现了所有要用到的通用数学运算方法:

    float Distance(Vector2 value1, Vector2 value2)
    float Dot(const Vector2 &left, const Vector2 &right)
    float Magnitude(const Vector2 &value)
    Vector2 Lerp(const Vector2 & start, const Vector2 & end, float amount)
    Vector2 Normalize(const Vector2 &value)
    Vector2 Reflect(Vector2 vector, Vector2 normal)
    
    
    Vector3 Cross(const Vector3 &left, const Vector3 &right)
    float Distance(Vector3 value1, Vector3 value2)
    float Dot(const Vector3 &left, const Vector3 &right)
    float Magnitude(const Vector3 &value)
    Vector3 Normalize(const Vector3 &value)
    Vector3 Lerp(const Vector3 & start, const Vector3 & end, float amount)
    Vector3 Reflect(Vector3 vector, Vector3 normal)
    Vector3 TransformCoordinate(Vector3 &coord, Matrix4x4 &transMat)
    
    Matrix4x4 LookAtRH(Vector3 &cameraPos, Vector3 &target, Vector3 &up)
    Matrix4x4 LookAtLH(Vector3 &cameraPos, Vector3 &target, Vector3 &up)
    Matrix4x4 Translation(Vector3 &value)
    Matrix4x4 PerspectiveFovRH(float fov, float aspect, float znear, float zfar)
    Matrix4x4 PerspectiveFovLH(float fov, float aspect, float znear, float zfar)
    Matrix4x4 RotationX(float angle)
    Matrix4x4 RotationY(float angle)
    Matrix4x4 RotationZ(float angle)
    Matrix4x4 RotationYawPitchRoll(float yaw, float pitch, float roll)
    Matrix4x4 Rotate(Vector3 &rotate)
    Matrix4x4 Scale(float x, float y, float z)
    Matrix4x4 Scale(Vector3 &scale)


    以上再结合c++的数学库可以满足这个demo的基本需求。





    github地址:https://github.com/wenshuiqing/Render3D

    展开全文
  • OpenSWR —— 用于OpenGL的高性能,高度可扩展的软件光栅化渲染器OpenSWR的目的是提供一个高性能,高度可扩展的OpenGL兼容软件光栅化渲染器,允许使用未经修改的可视化软件。 这允许使用GPU硬件不可用或限制的数据集...
  • 开发环境:VSCode+Chrome浏览器 ...软件光栅化渲染器,是指用CPU(软件)实现GPU(硬件)绘图的功能。在现实环境中,因为OpenGL/Direct3D/游戏引擎的存在,我们基本接触不到光栅化过程中的细节,通过自己用纯代码实

    项目代码:https://github.com/foupwang/JavaScript3DRenderer
    开发环境:VSCode+Chrome浏览器
    参考:《Windows游戏编程大师技巧》(第2版) /《3D游戏编程大师技巧》(André LaMothe)
    QQ交流群:1148938167(欢迎加入探讨图形渲染技术)

    软件光栅化渲染器,是指用CPU(软件)实现GPU(硬件)绘图的功能。在现实环境中,因为OpenGL/Direct3D/游戏引擎的存在,我们基本接触不到光栅化过程中的细节,通过自己用纯代码实现光栅化,可以熟悉常用的图形算法,对渲染管线有更深刻的认识。

    我将从一个最简单的画点函数开始,一小节一小节地实践怎么画一条直线,怎么填充三角形,怎么逐步加入背面剔除、深度测试、透视相机、着色器等高级功能。

    本项目大部分代码基于André LaMothe大神写的《Windows游戏编程大师技巧》(第2版)和《3D游戏编程大师技巧》,原书的C/C++代码,我把它改成了JavaScript实现,因为JavaScript运行环境简单,只要有浏览器就可以。且无需任何编译,打开就能运行。

    展开全文
  • 0.光栅化渲染器概述 主要实现了以下功能: 2D部分: 光栅化2D点 光栅化2D直线 光栅化2D三角形 3D部分: 把顶点从三维世界空间变换至二维屏幕空间,把顶点连线(如各种三维正多面体)光栅化成wire frame模型 ...

    0.光栅化渲染器概述

    主要实现了以下功能:

    2D部分:

    • 光栅化2D点
    • 光栅化2D直线
    • 光栅化2D三角形

    3D部分:

    • 把顶点从三维世界空间变换至二维屏幕空间,把顶点连线(如各种三维正多面体)光栅化成wire frame模型
    • 三角形光栅化
    • 使用深度缓冲
    • 实现简单的纹理映射,先做屏幕空间的插值,然后实现简单的透视纹理校正
    • 实现简单的顶点光照,使用顶点颜色插值实现Gouraud shading

    具体参考以下前辈的建议与实现

    Milo Yip
    韦易笑
    萧井陌 ·
    从零实现3D图像引擎

    1.windows窗口结构

    要创建一个windows窗口我们得先对窗口结构有一定的了解,MSDN官网:
    MSDN WNDCLASSEX structure
    这里做一个简要说明

    typedef struct tagWNDCLASSEX {
      UINT      cbSize;             //窗口大小(count of byte),设为 sizeof(WNDCLASSEX)
      UINT      style;              //窗口风格
      WNDPROC   lpfnWndProc;        //指向窗口过程函数的函数指针
      int       cbClsExtra;         //窗口类附加内存
      int       cbWndExtra;         //窗口附加内存
      HINSTANCE hInstance;          //指定包含窗口过程的实例句柄
      HICON     hIcon;              //图标句柄
      HCURSOR   hCursor;            //光标句柄
      HBRUSH    hbrBackground;      //背景画刷句柄
      LPCTSTR   lpszMenuName;       //菜单资源的名字
      LPCTSTR   lpszClassName;      //窗口类的名字
      HICON     hIconSm;            //窗口类小图标句柄
    } WNDCLASSEX, *PWNDCLASSEX;

    2.一个完整的示例

    #include <windows.h>
    #include <assert.h>
    #include <string>
    #include <WindowsX.h>
    
    using namespace std;
    const int WIDTH = 800;
    const int HEIGHT = 800;
    
    LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lParam)
    {
        PAINTSTRUCT ps;
        HDC hdc;
        switch (message)
        {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_KEYDOWN:
            if (wparam==VK_ESCAPE)
            {
                DestroyWindow(hwnd);
            }
            break;
        default:
            return DefWindowProc(hwnd,message,wparam,lParam);
        }
        return 0;
    }
    
    HWND GameStart(HINSTANCE hInstance,int nShowCmd,string wcName,string title)
    {
        //1.创建窗口类
        WNDCLASSEX wndClass = {};
        wndClass.cbSize = sizeof(WNDCLASSEX);
        wndClass.style = CS_HREDRAW | CS_VREDRAW;
        wndClass.lpfnWndProc = WndProc;
        wndClass.cbClsExtra = 0;
        wndClass.cbWndExtra = 0;
        wndClass.hInstance = hInstance;
        wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
        wndClass.lpszMenuName = NULL;
        wndClass.lpszClassName = wcName.c_str();
    
        //2.注册窗口类
        assert(RegisterClassEx(&wndClass));
    
        //3.创建窗口
        HWND hwnd = CreateWindow(wcName.c_str(),title.c_str(),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,WIDTH,HEIGHT,NULL,NULL,hInstance,NULL);
    
        //4.调整大小,移动,显示,更新
        RECT window_rect = {0,0,WIDTH,HEIGHT};
        AdjustWindowRectEx(&window_rect, GetWindowStyle(hwnd), GetMenu(hwnd) != NULL, GetWindowExStyle(hwnd));
        MoveWindow(hwnd,300,150,window_rect.right-window_rect.left, window_rect.bottom-window_rect.top,false);
        ShowWindow(hwnd,nShowCmd);
        UpdateWindow(hwnd);
    
        return hwnd;
    }
    
    void GameUpdate(HWND hwnd)
    {
        /*图形绘制*/
    }
    
    void GameEnd(string wcName,HINSTANCE hInstance)
    {
        //5.注销窗口类
        UnregisterClass(wcName.c_str(),hInstance);
    }
    
    
    int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd )
    {
        //1.创建窗口
        string windowClassName = "MyWindow";
        string title = "3DRender";
        HWND hwnd = GameStart(hInstance,nShowCmd,windowClassName,title);
    
        //时间初始化
        DWORD curTime = GetTickCount();
        DWORD preTime = GetTickCount();
        //2.消息循环
        MSG msg = {0};
        while (msg.message!=WM_QUIT)
        {
            //获取消息
            if (PeekMessage(&msg,0,NULL,NULL,PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else
            {
                curTime = GetTickCount();
                if (curTime-preTime>30)
                {
                    preTime = curTime;
                    GameUpdate(hwnd);
                }
            }
        }
    
        //3.游戏结束
        GameEnd(windowClassName,hInstance);
        return 0;
    }

    以下是效果,如果出现字符错误,可以在VS中选择项目->属性->配置属性->常规->字符集->使用多字节字符集。

    3.项目完整地址:

    3DRender: https://github.com/zhanghuanzj/3DRender.git

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 如何开始用 C++ 写一个光栅化渲染器?: http://www.zhihu.com/question/24786878
  • 通过本文,您将会实现一个简单的软件光栅化渲染器,并会进一步理解渲染流水线所做的事。本文会通过一个简单的样例介绍以下知识: 1.纹理 2.深度缓冲 3.坐标变换 4.基础光照(漫反射光照,镜面光照,环境光照)。 5....
  • (如果你有模型文件或者手撸模型的话) 一个最基本的光栅化渲染器要实现的功能有: 背面剔除(把背面朝向屏幕的三角形剔除掉) 三角形切割(把一个三角形切割成两个平底或平顶的三角形) 深度检测(去掉被遮住的像素...
  • 这是一个用C#+winform实现的软件光栅化渲染器,今天拿出来与大家分享一下,希望能起到抛砖引玉的作用,给新人一点启发(结构比较简单,注释比较详细^_^),也欢迎司机们拍砖指点和交流~ 目的: 巩固图形编程知识,...
  • 这是一个用C#+winform实现的软件光栅化渲染器,今天拿出来与大家分享一下,希望能起到抛砖引玉的作用,给新人一点启发(结构比较简单,注释比较详细^_^)(新人+1,也给我很多启发),也欢迎司机们拍砖指点和交流~ ...
  • 一个简单的c++软光栅渲染器 #主要特性 数学库:常用向量float4,float3,float2,矩阵matrix,矩阵变换 模型标准:同d3d坐标模型 左手系+world view proj矩阵 实现背面消隐 简单CVV裁剪 深度缓存:判断图像前后位置 支持...
  • 1.准备工作光栅化渲染器的第一步就是绘制一个像素点,然后再进行线,三角形的绘制,这里主要通过directx9(具体哪个版本不重要,主要是用来绘制,也可以通过其它方式)进行像素点的绘制。//DirectX是自己定义的一个...
  • 所以我只实现了最简单的三顶点均不在CVV内剔除的方案 参考 从迷你光栅化渲染器的实现看渲染流水线 背面剔除 正向背向面剔除可以在NDC中进行,先计算三角形表面法向量,根据法向量和view向量的夹角进行剔除: enum ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 498
精华内容 199
关键字:

光栅化渲染器