我们已经完善了光照的参数,以及模型空间-世界空间-视角空间-裁剪空间-屏幕空间的转换。并且搭建了我们demo的框架,即一个正方体的各部分的信息结构,如顶点+索引+贴图+材质等。这次,我们来把一切联系起来,创建出真正的软渲染demo。
在上述结构中,我们定义出Moe3DDevice与Moe3DDeviceContext,用来表示设备信息。
设备上下文DC是一个Windows数据结构,它包含了某个设备的绘制属性。通常,绘制调用都是借助于上下文对象,而这些设备上下文对象封装了用于画线、形状、文本等的Windows API。设备上下文是设备无关的,所以它既可以用于绘制屏幕,也可以用于绘制打印机甚至元文件。设备上下文在内存中创建,而内存经常受到扰动,所以它的地址是不固定的。因此,一个设备上下文句柄不是直接指向设备上下文对象,而是指向另外一个跟踪设备上下文地址的指针。
我个人认为设备上下文相当于画图过程中的画布(画纸),在VS中,这个画布可以是显示器,也可以使打印机,设备上下文决定了画布的属性,而且封装了在画布上画画的方法,比如画线,画点,等等,例如: pDc->LineTo(512,0); //从左下角到右上角的一条红色直线 。我们在VS中画图时,首先要得到这块画布才可以画画,所以要进行获取设备环境。
1.1Moe3DDevice.h 描述设备信息
#pragma once
#include <windows.h>
#include "MVector.h"
#include "Vertex.h"
class Moe3DDevice
{
public:
Moe3DDevice(int width, int height);
~Moe3DDevice();
public:
void DrawPixel(int x, int y, MVector color);
float GetZ(int x, int y) const;
void SetZ(int x, int y, float z);
inline UINT*& GetFrameBuffer() { return m_pFramebuffer; }
inline int GetClientWidth() { return m_width; }
inline int getClientHeight() { return m_height; }
void ClearBuffer(MVector color);
private:
int m_width;
int m_height;
UINT* m_pFramebuffer;
float **m_zBuffer; //z缓存
};
1.2Moe3DDevice.cpp 获取设备信息
#include "Moe3DDevice.h"
#include "MathUtil.h"
using namespace MathUtil;
Moe3DDevice::Moe3DDevice(int width, int height)
{
m_width = width;
m_height = height;
m_zBuffer = new float*[width];
for (int i = 0; i < width; ++i)
{
m_zBuffer[i] = new float[height];
}
}
Moe3DDevice::~Moe3DDevice()
{
if (m_pFramebuffer)
delete m_pFramebuffer;
if (m_zBuffer)
for (int i = 0; i < m_width; ++i)
{
delete[] m_zBuffer[i];
}
}
//画像素
void Moe3DDevice::DrawPixel(int x, int y, MVector color)
{
m_pFramebuffer[m_width*y + x] = MathUtil::ColorToUINT(color);
}
float Moe3DDevice::GetZ(int x, int y) const
{
if (x >= 0 && x < m_width && y >= 0 && y < m_height)
return m_zBuffer[x][y];
else
return 1.f;
}
void Moe3DDevice::SetZ(int x, int y, float z)
{
if (x >= 0 && x < m_width && y >= 0 && y < m_height)
{
m_zBuffer[x][y] = z;
}
}
void Moe3DDevice::ClearBuffer(MVector color)
{
for (int x = 0; x < m_width; ++x)
{
for (int y = 0; y < m_height; ++y)
{
m_pFramebuffer[m_width*y + x] = MathUtil::ColorToUINT(color);
m_zBuffer[x][y] = 0;
}
}
}
2.1Moe3DDeviceContext.h 相当于在此作画
#pragma once
#include "Moe3DDevice.h"
#include "Vertex.h"
#include <vector>
#include "ShaderBase.h"
enum MOE3D_FILL_MODE //渲染模式
{
MOE3D_FILL_WIREFRAME,//线框模式
MOE3D_FILL_SOLIDE //实体模式
};
class Moe3DDeviceContext
{
public:
Moe3DDeviceContext();
~Moe3DDeviceContext();
public:
void Init(Moe3DDevice* pDevice); //初始化
void SetRenderMode(MOE3D_FILL_MODE mode); //设置渲染模式
void SetVertexBuffer(std::vector<VertexIn> vertices); //设置顶点缓冲
void SetCameraPos(const MVector& pos); //设置相机位置
void SetIndexBuffer(std::vector<UINT> indices); //设置索引缓冲
void SetShader(ShaderBase* base); //设置着色器
void DrawIndexed(UINT indexCount,UINT startIndexLocation,UINT startVertexLocation); //绘制图形
private:
void ToCVV(VertexOut& v); //投影后的坐标转化为cvv
bool Clip(const VertexOut& v); //cvv裁剪
VertexOut TransformToProj(const VertexIn& v); //转到齐次裁剪空间
void TransformToScreen(const MMatrix& m,VertexOut& v); //转换到屏幕坐标
bool BackFaceCulling(const VertexIn& v1, const VertexIn& v2, const VertexIn& v3); //背面消隐测试
void BresenhamDrawLine(int x1, int y1, int x2, int y2); //画线
void ScanlineFill(const VertexOut& left, const VertexOut& right, int yIndex); //扫描线
void DrawTriangle(const VertexOut& v1, const VertexOut& v2, const VertexOut& v3); //画三角形
void DrawTriangleTop(const VertexOut& v1, const VertexOut& v2, const VertexOut& v3); //画平顶三角形
void DrawTriangleBottom(const VertexOut& v1, const VertexOut& v2, const VertexOut& v3); //画平底三角形
void TriangleRasterization(const VertexOut& v1, const VertexOut& v2, const VertexOut& v3); //光栅化三角形
private:
Moe3DDevice* m_pDevice; //设备
MOE3D_FILL_MODE m_renderMode; //渲染状态
std::vector<VertexIn> m_vertices; //顶点缓冲
std::vector<UINT> m_indices; //索引缓冲
ShaderBase* m_pShader; //着色器
MVector m_cameraPos; //相机位置 用于背面消隐
};
2.2Moe3DDeviceContext.h 具体实现
#include "Moe3DDeviceContext.h"
#include "MathUtil.h"
#include <algorithm>
Moe3DDeviceContext::Moe3DDeviceContext():m_renderMode(MOE3D_FILL_WIREFRAME),m_cameraPos(MVector(0.f,0.f,0.f,1.f))
{
}
Moe3DDeviceContext::~Moe3DDeviceContext()
{
}
void Moe3DDeviceContext::Init(Moe3DDevice* pDevice)
{
m_pDevice = pDevice;
}
//设置渲染模式
void Moe3DDeviceContext::SetRenderMode(MOE3D_FILL_MODE mode)
{
m_renderMode = mode;
}
//设置顶点缓冲
void Moe3DDeviceContext::SetVertexBuffer(std::vector<VertexIn> vertices)
{
m_vertices = vertices;
}
//设置相机位置
void Moe3DDeviceContext::SetCameraPos(const MVector& pos)
{
m_cameraPos = pos;
}
//设置索引缓冲
void Moe3DDeviceContext::SetIndexBuffer(std::vector<UINT> indices)
{
m_indices = indices;
}
//设置着色器
void Moe3DDeviceContext::SetShader(ShaderBase* base)
{
m_pShader = base;
}
//绘制顶点缓冲中的三角形
void Moe3DDeviceContext::DrawIndexed(UINT indexCount, UINT startIndexLocation, UINT startVertexLocation)
{
//得到屏幕变换矩阵
MMatrix screenTransformMat = MathUtil::MMatrixScreenTransform(m_pDevice->GetClientWidth(),
m_pDevice->getClientHeight());
for (int i = startIndexLocation; i < indexCount / 3; ++i)
{
VertexIn p1 = m_vertices[startVertexLocation + m_indices[3 * i]];
VertexIn p2 = m_vertices[startVertexLocation + m_indices[3 * i + 1]];
VertexIn p3 = m_vertices[startVertexLocation + m_indices[3 * i + 2]];
//背面消隐
if (BackFaceCulling(p1, p2, p3) == false)
{
continue;
}
//转换到齐次裁剪空间,即投影后的坐标
VertexOut v1 = TransformToProj(p1);
VertexOut v2 = TransformToProj(p2);
VertexOut v3 = TransformToProj(p3);
//判断是否通过cvv裁剪
if (Clip(v1) == false || Clip(v2) == false || Clip(v3) == false)
{
continue;
}
//进行透视除法 转到cvv
ToCVV(v1);
ToCVV(v2);
ToCVV(v3);
//将投影得到的坐标转化为屏幕坐标
TransformToScreen(screenTransformMat, v1);
TransformToScreen(screenTransformMat, v2);
TransformToScreen(screenTransformMat, v3);
DrawTriangle(v1, v2, v3);
}
}
//转化到cvv
void Moe3DDeviceContext::ToCVV(VertexOut& v)
{
v.posH.x /= v.posH.w;
v.posH.y /= v.posH.w;
v.posH.z /= v.posH.w;
v.posH.w = 1;
}
//简单cvv裁剪
bool Moe3DDeviceContext::Clip(const VertexOut& v)
{
//cvv为 x-1,1 y-1,1 z0,1
if (v.posH.x >= -v.posH.w && v.posH.x <= v.posH.w &&
v.posH.y >= -v.posH.w && v.posH.y <= v.posH.w &&
v.posH.z >= 0.f && v.posH.z <= v.posH.w)
{
return true;
}
return false;
}
//转到齐次裁剪空间
VertexOut Moe3DDeviceContext::TransformToProj(const VertexIn& v)
{
VertexOut out = m_pShader->VS(v);
//设置oneDivZ
out.oneDivZ = 1 / out.posH.w;
//由于1/z和x,y成线性关系
//这里将需要插值的信息都乘以1/z 得到 s/z和t/z等,方便光栅化阶段进行插值
out.color.x *= out.oneDivZ;
out.color.y *= out.oneDivZ;
out.color.z *= out.oneDivZ;
out.normal.x *= out.oneDivZ;
out.normal.y *= out.oneDivZ;
out.normal.z *= out.oneDivZ;
out.tex.u *= out.oneDivZ;
out.tex.v *= out.oneDivZ;
return out;
}
//转换到屏幕坐标
void Moe3DDeviceContext::TransformToScreen(const MMatrix& m, VertexOut& v)
{
v.posH = v.posH * m;
}
//背面消隐
bool Moe3DDeviceContext::BackFaceCulling(const VertexIn& v1, const VertexIn& v2, const VertexIn& v3)
{
//线框模式不进行背面消隐
if (m_renderMode == MOE3D_FILL_WIREFRAME)
{
return true;
}
else
{
MVector vector1 = v2.pos - v1.pos;
MVector vector2 = v3.pos - v2.pos;
//顶点缓存中顺序为顺时针
//叉积得到的方向与右手系一致
MVector normal = vector1.Cross(vector2);
MVector viewDir = v1.pos - m_cameraPos;
if (normal.Dot(viewDir) < 0)
{
return true;
}
return false;
}
}
//画三角形
void Moe3DDeviceContext::DrawTriangle(const VertexOut& v1, const VertexOut& v2, const VertexOut& v3)
{
//线框模式
if (m_renderMode == MOE3D_FILL_WIREFRAME)
{
BresenhamDrawLine(v1.posH.x, v1.posH.y, v2.posH.x, v2.posH.y);
BresenhamDrawLine(v1.posH.x, v1.posH.y, v3.posH.x, v3.posH.y);
BresenhamDrawLine(v2.posH.x, v2.posH.y, v3.posH.x, v3.posH.y);
}
else if (m_renderMode == MOE3D_FILL_SOLIDE)
{
TriangleRasterization(v1, v2, v3);
}
}
//bresenham画线
void Moe3DDeviceContext::BresenhamDrawLine(int x1, int y1, int x2, int y2)
{
int dx = x2 - x1;
int dy = y2 - y1;
int stepx = 1;
int stepy = 1;
if (dx >= 0)
{
stepx = 1;
}
else
{
stepx = -1;
dx = abs(dx);
}
if (dy >= 0)
{
stepy = 1;
}
else
{
stepy = -1;
dy = abs(dy);
}
int deltaX = 2 * dx;
int deltaY = 2 * dy;
if (dx > dy)
{
int error = deltaY - dx;
for (int i = 0; i <= dx; ++i)
{
if(x1 >= 0 && x1 < m_pDevice->GetClientWidth() && y1 >= 0 && y1 < m_pDevice->getClientHeight())
m_pDevice->DrawPixel(x1, y1, MVector(0.f, 0.f, 0.f,1.f));
if (error >= 0)
{
error -= deltaX;
y1 += stepy;
}
error += deltaY;
x1 += stepx;
}
}
else
{
int error = deltaX - dy;
for (int i = 0; i <= dy; i++)
{
if (x1 >= 0 && x1 < m_pDevice->GetClientWidth() && y1 >= 0 && y1 < m_pDevice->getClientHeight())
m_pDevice->DrawPixel(x1, y1, MVector(0.f, 0.f, 0.f,1.f));
if (error >= 0)
{
error -= deltaY;
x1 += stepx;
}
error += deltaX;
y1 += stepy;
}
}
}
//扫描线填充
//left 左端点 right 右端点
void Moe3DDeviceContext::ScanlineFill(const VertexOut& left, const VertexOut& right, int yIndex)
{
float dx = right.posH.x - left.posH.x;
for (float x = left.posH.x; x <= right.posH.x; x += 1.f)
{
//四舍五入
int xIndex = static_cast<int>(x + .5f);
if(xIndex >= 0 && xIndex < m_pDevice->GetClientWidth())
{
//插值系数
float lerpFactor = 0;
if (dx != 0)
{
lerpFactor = (x - left.posH.x) / dx;
}
//深度测试
//1/z’与x’和y'是线性关系的
float oneDivZ = MathUtil::Lerp(left.oneDivZ, right.oneDivZ, lerpFactor);
if (oneDivZ >= m_pDevice->GetZ(xIndex,yIndex))
{
m_pDevice->SetZ(xIndex, yIndex, oneDivZ);
float w = 1 / oneDivZ;
//插值顶点 原先需要插值的信息均乘以oneDivZ
//现在得到插值后的信息需要除以oneDivZ得到真实值
VertexOut out = MathUtil::Lerp(left, right, lerpFactor);
out.posH.y = yIndex;
out.tex.u *= w;
out.tex.v *= w;
out.normal.x *= w;
out.normal.y *= w;
out.normal.z *= w;
out.color.x *= w;
out.color.y *= w;
out.color.z *= w;
//画像素点颜色
m_pDevice->DrawPixel(xIndex, yIndex, m_pShader->PS(out));
}
}
}
}
//画平顶三角形 v3为下顶点
//y方向每次增加一个像素 根据y插值顶点
void Moe3DDeviceContext::DrawTriangleTop(const VertexOut& v1, const VertexOut& v2, const VertexOut& v3)
{
float dy = 0;//每次y增加一个像素
for (float y = v1.posH.y; y <= v3.posH.y; y += 1.f)
{
//四舍五入
int yIndex = static_cast<int>(y + 0.5f);
if (yIndex >= 0 && yIndex < m_pDevice->getClientHeight())
{
float t = dy / (v3.posH.y - v1.posH.y);
//插值生成左右顶点
VertexOut new1 = MathUtil::Lerp(v1, v3, t);
VertexOut new2 = MathUtil::Lerp(v2, v3, t);
dy += 1.f;
//扫描线填充
if (new1.posH.x < new2.posH.x)
{
ScanlineFill(new1, new2, yIndex);
}
else
{
ScanlineFill(new2, new1, yIndex);
}
}
}
}
//画平底三角形 v1为上顶点
void Moe3DDeviceContext::DrawTriangleBottom(const VertexOut& v1, const VertexOut& v2, const VertexOut& v3)
{
float dy = 0;//每次y增加一个像素
for (float y = v1.posH.y; y <= v2.posH.y; y += 1.f)
{
//四舍五入
int yIndex = static_cast<int>(y + 0.5f);
if (yIndex >= 0 && yIndex < m_pDevice->getClientHeight())
{
float t = dy / (v2.posH.y - v1.posH.y);
//插值左右顶点
VertexOut new1 = MathUtil::Lerp(v1, v2, t);
VertexOut new2 = MathUtil::Lerp(v1, v3, t);
dy += 1.f;
//扫描线填充
if (new1.posH.x < new2.posH.x)
{
ScanlineFill(new1, new2, yIndex);
}
else
{
ScanlineFill(new2, new1, yIndex);
}
}
}
}
//光栅化三角形
void Moe3DDeviceContext::TriangleRasterization(const VertexOut& v1, const VertexOut& v2, const VertexOut& v3)
{
//判断是否是平底或者平顶三角形
if (v1.posH.y == v2.posH.y)
{
if (v1.posH.y < v3.posH.y)
{//平顶
DrawTriangleTop(v1, v2, v3);
}
else
{//平底
DrawTriangleBottom(v3, v1, v2);
}
}
else if (v1.posH.y == v3.posH.y)
{
if (v1.posH.y < v2.posH.y)
{//平顶
DrawTriangleTop(v1, v3, v2);
}
else
{//平底
DrawTriangleBottom(v2, v1, v3);
}
}
else if (v2.posH.y == v3.posH.y)
{
if (v2.posH.y < v1.posH.y)
{//平顶
DrawTriangleTop(v2, v3, v1);
}
else
{//平底
DrawTriangleBottom(v1, v2, v3);
}
}
//一般三角形 将其分割成平底三角形和平顶三角形
else
{
//根据y值将三个顶点排序
std::vector<VertexOut> vertices{v1,v2,v3};
std::sort(vertices.begin(), vertices.end(), [](VertexOut v1,VertexOut v2) {
return v1.posH.y < v2.posH.y;});
VertexOut top = vertices[0];
VertexOut middle = vertices[1];
VertexOut bottom = vertices[2];
//插值求中间点
float middleX = (middle.posH.y - top.posH.y) * (bottom.posH.x - top.posH.x) /
(bottom.posH.y - top.posH.y) + top.posH.x;
float dy = middle.posH.y - top.posH.y;
float t = dy / (bottom.posH.y - top.posH.y);
VertexOut newMiddle = MathUtil::Lerp(top, bottom, t);
newMiddle.posH.x = middleX;
newMiddle.posH.y = middle.posH.y;
//平顶
DrawTriangleTop(middle, newMiddle, bottom);
//平底
DrawTriangleBottom(top, middle, newMiddle);
}
}
3.1BoxDemo.cpp 实现我们的demo
过程:顶点->索引->着色器->纹理->光源->材质。
并且实现鼠标操作,来改变我们的视角。
#include <vector>
#include "BoxDemo.h"
#include "BoxShader.h"
#include "Moe3DDevice.h"
#include "Moe3DDeviceContext.h"
BoxDemo::BoxDemo():m_theta(1.5f * MathUtil::PI),m_phi(0.4*MathUtil::PI),m_radius(5.0f)
{
m_lastMousePos.x = 0;
m_lastMousePos.y = 0;
m_world = MathUtil::MMatrixIdentity();
//平行光
m_dirLight.ambient = MVector(0.2f, 0.2f, 0.2f, 1.0f);
m_dirLight.diffuse = MVector(0.5f, 0.5f, 0.5f, 1.0f);
m_dirLight.specular = MVector(0.5f, 0.5f, 0.5f, 1.0f);
m_dirLight.direction = MVector(0.57735f, 0.57735f, 0.57735f);
//材质
m_material.ambient = MVector(0.7f, 0.85f, 0.7f, 1.0f);
m_material.diffuse = MVector(0.7f, 0.85f, 0.7f, 1.0f);
m_material.specular = MVector(0.8f, 0.8f, 0.8f, 16.0f);
}
BoxDemo::~BoxDemo()
{
Clear();
}
bool BoxDemo::Init(HINSTANCE hInstance,HWND hWnd)
{
m_hWnd = hWnd;
m_hInstance = hInstance;
RECT rc;
GetClientRect(m_hWnd, &rc);
m_width = rc.right - rc.left;
m_height = rc.bottom - rc.top;
m_pDevice = new Moe3DDevice(m_width, m_height);
m_pImmediateContext = new Moe3DDeviceContext();
m_pImmediateContext->Init(m_pDevice);
m_pShader = new BoxShader();
//创建顶点缓存
GeometryGenerator::GetInstance()->CreateBox(2.f, 2.f, 2.f, m_box);
m_vertices.resize(m_box.vertices.size());
for (UINT i = 0; i < m_box.vertices.size(); ++i)
{
m_vertices[i].pos = m_box.vertices[i].pos;
m_vertices[i].tex = m_box.vertices[i].tex;
m_vertices[i].normal = m_box.vertices[i].normal;
m_vertices[i].color = m_box.vertices[i].color;
}
m_pImmediateContext->SetVertexBuffer(m_vertices);
//创建索引缓存
//create index buffer
m_indices.resize(m_box.indices.size());
for (UINT i = 0; i < m_box.indices.size(); ++i)
{
m_indices[i] = m_box.indices[i];
}
m_pImmediateContext->SetIndexBuffer(m_indices);
//设置着色器
m_pImmediateContext->SetShader(m_pShader);
//加载纹理
m_tex = MathUtil::LoadBitmapToColorArray(L"../Texture/nico.bmp");
//设置着色器纹理
//由于纹理加载一次不在改变,故不用再Update中设置
m_pShader->SetTexture(m_tex);
//设置着色器光源、材质
m_pShader->SetDirLight(m_dirLight);
m_pShader->SetMaterial(m_material);
return true;
}
void BoxDemo::Update(float dt)
{
float x = m_radius * sinf(m_phi) * cosf(m_theta);
float z = m_radius * sinf(m_phi) * sinf(m_theta);
float y = m_radius * cosf(m_phi);
MVector pos(x, y, z, 1.f);
MVector target(0.f, 0.f, 0.f, 1.f);
MVector up(0.f, 1.f, 0.f, 0.f);
MMatrix view = MathUtil::MMatrixLookAtLH(pos, target, up);
MMatrix proj = MathUtil::MMatrixPerspectiveFovLH(MathUtil::PI / 4, m_pDevice->GetClientWidth() /
static_cast<float>(m_pDevice->getClientHeight()), 1.f, 100.f);
MMatrix world = MathUtil::MMatrixIdentity();
m_worldViewProj = world*view*proj;
m_world = world;
//计算逆矩阵的转置
m_worldInvTranspose = MathUtil::MMatrixTranspose(MathUtil::MMatrixInverse(world));
//设置相机位置 以便背面消隐
m_pImmediateContext->SetCameraPos(pos);
//设置着色器中eyePos
m_pShader->SetEyePos(pos);
}
void BoxDemo::Render()
{
//清空后缓冲图片
m_pDevice->ClearBuffer(MVector(0.75f, 0.75f, 0.75f,1.f));
//设置渲染状态
m_pImmediateContext->SetRenderMode(MOE3D_FILL_SOLIDE);
//设置着色器变量
m_pShader->SetWorldViewProj(m_worldViewProj);
m_pShader->SetWorld(m_world);
m_pShader->SetWorldInvTranspose(m_worldInvTranspose);
m_pImmediateContext->DrawIndexed(m_indices.size(), 0, 0);
}
void BoxDemo::Clear()
{
if(m_pDevice)
delete m_pDevice;
if(m_pImmediateContext)
delete m_pImmediateContext;
if (m_pShader)
delete m_pShader;
}
void BoxDemo::OnMouseDown(WPARAM btnState, int x, int y)
{
m_lastMousePos.x = x;
m_lastMousePos.y = y;
SetCapture(m_hWnd);
}
void BoxDemo::OnMouseUp(WPARAM btnState, int x, int y)
{
ReleaseCapture();
}
void BoxDemo::OnMouseMove(WPARAM btnState, int x, int y)
{
if ((btnState & MK_LBUTTON) != 0)
{
//角度转弧度
float dx = MathUtil::MConvertToRadians(0.25f*static_cast<float>(x - m_lastMousePos.x));
float dy = MathUtil::MConvertToRadians(0.25f*static_cast<float>(y - m_lastMousePos.y));
m_theta -= dx;
m_phi += dy;
m_phi = MathUtil::Clamp(m_phi, 0.1f, MathUtil::PI - 0.1f);
}
else if ((btnState & MK_RBUTTON) != 0)
{
float dx = 0.2f*static_cast<float>(x - m_lastMousePos.x);
float dy = 0.2f*static_cast<float>(y - m_lastMousePos.y);
m_radius += dx - dy;
m_radius = MathUtil::Clamp(m_radius, 3.0f, 300.0f);
}
m_lastMousePos.x = x;
m_lastMousePos.y = y;
}
以上,我们已经完成基本所有的软渲染demo条件,包括软渲染的流程、设备上下文模拟、以及demo的结构。
- vs2017宇宙最伟大IDE用Console等调试汇总
- c++Win32起始鼠标作图181101
- 用迭代法找(两圆的)交点-精确计算迭代并改进-数值周期1810
- 精度-比例关系181110P2点
- 用迭代法求找两圆交点-精度计算181111A
- 月亮型-大小圆-上下圆算法181121
- 用c++的数学计算及图形绘制总结之1/共4-181101
- 用c++做数学计算及图形绘制总结之2/4-181102
- 用c++做数学计算及图形绘制总结之3/4-181103
- 用c++做数学计算及图形绘制总结4/4-181104
- 用c++的移位代替乘除运算181105
- 重构billiard2圆相交-非递归181101-非预料内图形-原因分析181201
- 重构月亮型billiard202圆相交-非递归181102-非预料内图形-原因分析181202
- 重构月亮型billiard202圆相交-非递归181102b-非预料内图形-原因分析181202b
- 单椭圆(非递归)18圣诞后-ok版181225