在了解了 SDL2 如何画点( http://blog.csdn.net/korekara88730/article/details/70880061 ) 的基础上,尝试实现一个渲染器。
先把基础的绘制封装到一个类里,操作起来方便些。这个 类就叫 MRender. 要实现的基本工程包括: 画点; 画线; 画三角形; 画简单3D 模型
今天先做了个简单的封装,把之前零散的逻辑放到 MRender 里面。这个类将来要根据功能,实现以下几个函数:
drawPoint(), drawLine(), drawTriangle(), drawModel()
由于需要反复操作自定义的 帧缓冲区, 本来打算帧缓冲区作为 MRender 的成员,以二维数组的形式存在。但是感觉不是很方便,于是新增加一个 类 MFrameBufferObject.这样 缓冲区 就可以 以对象的形式,组合进 MRender 里。
MRender 最终暴露出来一些接口,给 main.cpp 这种外围的代码调用。MRender要做成一个黑匣子,对外界透明。今天只简单的封装了一下,所以只有 最最简单的 画点的 功能.
剩下的功能会慢慢补上.
代码先贴在这
main.cpp
#include "MRender.h"
MRender render;
void draw()
{
for (int x = 0; x < 50;x++)
{
for (int y = 0; y < 50;y++)
{
render.setColor(0, 255, 0, 255);
render.drawPoint(x, y);
}
}
}
int main(int argc, char* args[])
{
if (!render.init())
{
return -1;
}
render.setClearColor(255, 0, 0, 255);
render.setDrawFunc(draw);
render.mainLoop();
render.cleanup();
return 0;
}
MRender.h
#ifndef __M_RENDER_H__
#define __M_RENDER_H__
#include "SDL.h"
#include <stdio.h>
#include <stdlib.h>
class MFrameBufferObject;
class MRender{
public:
MRender();
virtual ~MRender();
bool init();
void mainLoop();
void cleanup();
bool genBuffer();
void setClearColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a);
void setColor(Uint8 r,Uint8 g,Uint8 b,Uint8 a);
void drawPoint(int x,int y);
void drawLine(int x1, int y1, int x2, int y2);
void drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3);
private:
void drawFrameBuffer();
bool isCurrentBufferAvailable() const;
int getActiveBufferIndex() const { return _mActiveBufferIndex; }
private:
SDL_Window* _mWindow;
SDL_Surface* _mScreenSurface;
SDL_Renderer* _mRenderer;
private:
const static int MAX_FRAME_BUFFER_NUM = 3;
MFrameBufferObject* _mFrameBuffers[MAX_FRAME_BUFFER_NUM];
int _mActiveBufferIndex;
Uint8 _drawR, _drawG, _drawB, _drawA;
Uint8 _clearR, _clearG, _clearB, _clearA;
public:
void setDrawFunc(void(*drawFunc)());
private:
void (*_drawFunc)();
};
#endif //__M_RENDER_H__
MRender.cpp
#include "MRender.h"
#include "MFrameBufferObject.h"
const int SCREEN_WIDTH = 400;
const int SCREEN_HEIGHT = 400;
MRender::MRender()
:_mActiveBufferIndex(-1)
{
for (size_t i = 0; i < MAX_FRAME_BUFFER_NUM; i++)
{
_mFrameBuffers[i] = nullptr;
}
}
MRender::~MRender()
{
for (size_t i = 0; i < MAX_FRAME_BUFFER_NUM; i++)
{
if (_mFrameBuffers[i] != nullptr)
{
delete _mFrameBuffers[i];
_mFrameBuffers[i] = nullptr;
}
}
}
bool MRender::init()
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("SDL can not initialized!SDL Error:%s\n", SDL_GetError());
return false;
}
_mWindow = SDL_CreateWindow("msdl1", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (_mWindow == nullptr)
{
printf("SDL can't create window!SDL Error:%s\n", SDL_GetError());
return false;
}
_mScreenSurface = SDL_GetWindowSurface(_mWindow);
_mRenderer = SDL_CreateRenderer(_mWindow, -1, SDL_RENDERER_ACCELERATED);
if (_mRenderer == nullptr)
{
return false;
}
//SDL_FillRect(gScreenSurface, nullptr, SDL_MapRGBA(gScreenSurface->format, 0xff, 0xff, 0xff, 0xff));
SDL_FillRect(_mScreenSurface, nullptr, SDL_MapRGBA(_mScreenSurface->format, 0, 0, 0, 0));
genBuffer();
return true;
}
void MRender::mainLoop()
{
bool quit = false;
SDL_Event evt;
while (!quit)
{
while (SDL_PollEvent(&evt) != 0)
{
if (evt.type == SDL_QUIT){
quit = true;
}
}
SDL_RenderClear(_mRenderer);
drawFrameBuffer();
// present
SDL_RenderPresent(_mRenderer);
}
}
void MRender::cleanup()
{
SDL_DestroyRenderer(_mRenderer);
SDL_DestroyWindow(_mWindow);
SDL_Quit();
}
bool MRender::genBuffer()
{
if (getActiveBufferIndex() <= MAX_FRAME_BUFFER_NUM - 1)
{
_mActiveBufferIndex++;
if (_mFrameBuffers[_mActiveBufferIndex] == nullptr)
{
_mFrameBuffers[_mActiveBufferIndex] = new MFrameBufferObject(SCREEN_WIDTH, SCREEN_HEIGHT);
}
}
return false;
}
bool MRender::isCurrentBufferAvailable() const
{
return _mActiveBufferIndex >= 0 && _mActiveBufferIndex < MAX_FRAME_BUFFER_NUM;
}
void MRender::drawFrameBuffer()
{
if (!isCurrentBufferAvailable()){return;}
MFrameBufferObject* frameBuffer = _mFrameBuffers[_mActiveBufferIndex];
frameBuffer->setColor(_clearR, _clearG, _clearB, _clearA);
frameBuffer->clear();
if (_drawFunc != nullptr)
{
_drawFunc();
}
frameBuffer->setColor(_drawR, _drawG, _drawB, _drawA);
frameBuffer->draw(_mRenderer);
}
void MRender::setClearColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
_clearR = r; _clearG = g; _clearB = b; _clearA = a;
}
void MRender::setColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
_drawR = r; _drawG = g; _drawB = b; _drawA = a;
}
void MRender::drawPoint(int x, int y)
{
_mFrameBuffers[_mActiveBufferIndex]->setColorAt(x, y, _drawR, _drawG, _drawB, _drawA);
}
void MRender::drawLine(int x1, int y1, int x2, int y2)
{
}
void MRender::drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3)
{
}
void MRender::setDrawFunc(void(*drawFunc)())
{
_drawFunc = drawFunc;
}
MFrameBufferObject.h
#ifndef __M_FRAMEBUIFFER_OBJECT_H__
#define __M_FRAMEBUIFFER_OBJECT_H__
#include "SDL.h"
class MFrameBufferObject{
public:
MFrameBufferObject(int width, int height);
virtual ~MFrameBufferObject();
Uint8 getR(size_t x,size_t y);
Uint8 getG(size_t x, size_t y);
Uint8 getB(size_t x, size_t y);
Uint8 getA(size_t x, size_t y);
void setColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a);
void clear();
void setColorAt(size_t x,size_t y,Uint8 r,Uint8 g,Uint8 b,Uint8 a);
void draw(SDL_Renderer* pRenderer);
private:
void resetData();
int getBaseOffsetAt(size_t x,size_t y);
// R,G,B,A
size_t getOffsetR() const { return 0; }
size_t getOffsetG() const { return 1; }
size_t getOffsetB() const { return 2; }
size_t getOffsetA() const { return 3; }
private:
int _width;
int _height;
Uint8* _data;
Uint8 _colR, _colG, _colB, _colA;
};
#endif //__M_FRAMEBUIFFER_OBJECT_H__
MFrameBufferObject.cpp
#include "MFrameBufferObject.h"
#include <assert.h>
#include <stdio.h>
const int PIXEL_STRID = 4;// 4 means (R,G,B,A) 4 members
MFrameBufferObject::MFrameBufferObject(int width, int height)
:_width(width),
_height(height),
_colR(0x0),
_colG(0x0),
_colB(0x0),
_colA(0x0)
{
_data = new Uint8[_width * _height * PIXEL_STRID];
resetData();
}
MFrameBufferObject::~MFrameBufferObject()
{
if (_data){ delete _data; _data = nullptr; }
}
void MFrameBufferObject::resetData()
{
for (int y = 0; y < _height;y++)
{
for (int x = 0; x < _width;x++)
{
int offset = getBaseOffsetAt(x,y);
_data[offset + getOffsetR()] = _colR;
_data[offset + getOffsetG()] = _colG;
_data[offset + getOffsetB()] = _colB;
_data[offset + getOffsetA()] = _colA;
}
}
}
int MFrameBufferObject::getBaseOffsetAt(size_t x, size_t y)
{
assert(x >= 0 && x < _width && y >= 0 && y < _height);
return y * _width * PIXEL_STRID + x * PIXEL_STRID;
}
Uint8 MFrameBufferObject::getR(size_t x, size_t y)
{
return _data[getBaseOffsetAt(x,y) + getOffsetR()];
}
Uint8 MFrameBufferObject::getG(size_t x, size_t y)
{
return _data[getBaseOffsetAt(x, y) + getOffsetG()];
}
Uint8 MFrameBufferObject::getB(size_t x, size_t y)
{
return _data[getBaseOffsetAt(x, y) + getOffsetB()];
}
Uint8 MFrameBufferObject::getA(size_t x, size_t y)
{
return _data[getBaseOffsetAt(x, y) + getOffsetA()];
}
void MFrameBufferObject::setColorAt(size_t x, size_t y, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
int baseOffset = getBaseOffsetAt(x, y);
_data[baseOffset + getOffsetR()] = r;
_data[baseOffset + getOffsetG()] = g;
_data[baseOffset + getOffsetB()] = b;
_data[baseOffset + getOffsetA()] = a;
}
void MFrameBufferObject::setColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
_colR = r; _colG = g; _colB = b; _colA = a;
}
void MFrameBufferObject::clear()
{
resetData();
}
void MFrameBufferObject::draw(SDL_Renderer* pRenderer)
{
for (int y = 0; y < _height; y++)
{
for (int x = 0; x < _width; x++)
{
SDL_SetRenderDrawColor(pRenderer, getR(x,y),getG(x,y),getB(x,y),getA(x,y));
SDL_RenderDrawPoint(pRenderer, x, y);
}
}
}