DX11梯台&旋转运动

DX11梯台&旋转运动


目录


题目&要求
  • 1.绘制一个梯台

  • 2.使用键盘和鼠标来控制该梯台的旋转


存在问题
  • 虽然对xjun师兄的整体代码流程有了基本的了解,但是还存在一些问题
    • 1、可能对CPU、GPU以及缓冲区、内存读取资源等等原理不太了解。
    • 2、看xjun师兄原代码,对整体流程以及代码都差不多能说出来。但是自己重新写出来,现在还不能够。

学习情况&整体思路
  • 对图形渲染代码的初始化已经有的初步的认识。

  • 下面来说一下梯台在代码中实现的整体流程以及思路分析。

  • **1、**在Main函数中调用GameApp::Init()函数,同时GameApp和D3DApp的构造函数一起调用,

    • 对D3DApp内的数据进行初始化。
  • **2、**GameApp::Init()函数中调用D3DApp::Init()函数,然后调用里面InitMainWindow()函数,进行创建窗口。

    • 再调用InitDirect3D()函数创建D3D设备 、D3D设备上下文和交换链。(这里面的创建流程就不细说了)
  • **3、**接下来回到GameApp::Init()中调用InitEffect()函数(着色器或特效相关的初始化),里面创建了顶点着色器, 同时利用到HLSL编译。创建创建并绑定顶点布局。创建像素着色器,一样利用到HLSL编译。其中HR宏是对错误的追踪。

  • **4、**再调用InitResource()函数,

    • 1、创建顶点坐标数组。
    • 2、创建顶点缓冲区。
    • 3、 索引数组,并且创建索引缓冲区,再是输入装配阶段的索引缓冲区设置。
    • 4、创建常量缓冲区,初始化常量缓冲区的值。
    • 5、渲染管线各个阶段绑定好所需资源:输入装配阶段的顶点缓冲区设置、设置图元类型,设定输入布局、将着色器绑定到渲染管线、将更新好的常量缓冲区绑定到顶点着色器。
    • 6、到初始化鼠标。
    • 7、回到GameApp::Run()函数中,信息循环。
  • 5、:GameApp::UpdateScene(float dt)对鼠标状态的处理和利用,同时更新常量缓冲区,让立方体转起来。GameApp::DrawScene()绘制窗口背景,深度/模板视图,绘制图。

  • 对以上流程代码部分实现分析(其他的在前面渲染管线讲完了)。

    DX11 渲染管线&六边形绘制

一、索引缓冲区
    • 看过我前面那一章的,有没有发现方法二实现,是不是重复的顶点特别多,这样的绘制方法会占用大量的内存空间,那怎么办?,有问题就肯定有解决的方法。
    • 接下来会讲另外一种绘制方法,可以只提供立方体的8个顶点数据,然后用一个索引数组来指代使用哪些顶点,按怎样的顺序绘制。
  • 那就是使用索引缓冲区进行替代指定顺序绘制,可以有效减少顶点缓冲区的占用空间,避免提供大量重复的顶点数据。
  • 首先是数据,没有数据怎么行。
    // ******************
	// 设置立方体顶点
	//    5________ 6
	//    /|      /|
	//   /_|_____/ |
	//  1|4|_ _ 2|_|7
	//   | /     | /
	//   |/______|/
	//  0       3
	VertexPosColor vertices[] =
	{
		{ XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f) },
		{ XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
		{ XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },
		{ XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
		{ XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
		{ XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT4(1.0f, 0.0f, 1.0f, 1.0f) },
		{ XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f) },
		{ XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT4(0.0f, 1.0f, 1.0f, 1.0f) }
	};

......

    // ******************
	// 索引数组
	// 使用索引缓冲区进行替代指定顺序绘制,可以有效减少顶点缓冲区的占用空间,避免提供大量重复的顶点数据
	DWORD indices[] = {
		// 正面
		0, 1, 2,
		2, 3, 0,
		// 左面
		4, 5, 1,
		1, 0, 4,
		// 顶面
		1, 5, 6,
		6, 2, 1,
		// 背面
		7, 6, 5,
		5, 4, 7,
		// 右面
		3, 2, 6,
		6, 7, 3,
		// 底面
		4, 0, 3,
		3, 7, 4
	};
  • 万物即可三角形,每一个面是不是两个三角形 0、1、2 和2、3、0,你再参考一下上面的正方体的顶点位置,是正面吧。如果我没有用索引数组,是不是我上面设置立方体顶点那里需要36个顶点,像下面索引数组那样排布,数据量不是一般的大了。上一章方法二18个顶点就已经…

  • 然后是创建,和之前的顶点缓冲区的创建一样

	// 设置索引缓冲区描述
	D3D11_BUFFER_DESC ibd;
	ZeroMemory(&ibd, sizeof(ibd));//ZeroMemory其作用是用0来填充一块内存区域,将结构中所有字节置0,初始化重置,没有用的参数必须要设为零
	ibd.Usage = D3D11_USAGE_IMMUTABLE;//CPU和GPU的读写权限相关
	ibd.ByteWidth = sizeof indices;//数据字节数
	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;//缓冲区类型的标志
	ibd.CPUAccessFlags = 0;//CPU读写权限的指定
	// 新建索引缓冲区
    //使用D3D11_SUBRESOURCE_DATA结构体来指定要用来初始化的数据
    //InitData是在上面顶点那里设置的对象
	InitData.pSysMem = indices;//初始化数据

    //最后通过ID3D11Device::CreateBuffer来创建一个索引缓冲区
	//([In]索引缓冲区描述,[In]子资源数据,[Out] 获取缓冲区)
	HR(m_pd3dDevice->CreateBuffer(&ibd, &InitData, m_pIndexBuffer.GetAddressOf()));
  • 设备那里创建了,那么设备上下文也设置一下了 之间关联(m_pIndexBuffer)

  • 在装配的时候你需要指定每个索引所占的字节数:

    DXGI_FORMAT字节数索引范围
    DXGI_FORMAT_R8_UINT10-255
    DXGI_FORMAT_R16_UINT20-65535
    DXGI_FORMAT_R32_UINT40-2147483647
    // 输入装配阶段的索引缓冲区设置
	// ([In]索引缓冲区,[In]数据格式,[In]字节偏移量)
	// DXGI_FORMAT_R32_UINT	 4(字节)	0-2147483647(索引范围)
	m_pd3dImmediateContext->IASetIndexBuffer(m_pIndexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0);
二、常量缓冲区
    • 发现了没有,这里的正方体会旋转。怎么实现的呢?
  • 那就是用到常量缓冲区了

  • 首先是在GameApp.h中,也就是c++应用层这边定义一下旋转用的矩阵

  • 	struct ConstantBuffer
    	{
    		DirectX::XMMATRIX world;   //世界坐标
    		DirectX::XMMATRIX view;    //观察
    		DirectX::XMMATRIX proj;    //投射,射影
    	};
    
  • 单单在c++这边怎么可以,也要供着色器代码使用,那就在HLSL中声明一下

  • //cbuffer 用于声明一个常量缓冲区
    //register(b0) 指的是该常量缓冲区位于寄存器索引为0的缓冲区
    cbuffer ConstantBuffer : register(b0)
    {
        matrix g_World; // matrix可以用float4x4替代。不加row_major的情况下,矩阵默认为列主矩阵,
        matrix g_View;  // 可以在前面添加row_major表示行主矩阵
        matrix g_Proj;  // 该教程往后将使用默认的列主矩阵,但需要在C++代码端预先将矩阵进行转置。
    }
    
  • 正方体旋转,是不是需要频繁的更新。好,来看一下怎样描述

  • // ******************
    	// 设置常量缓冲区描述
    	//
        D3D11_BUFFER_DESC cbd;
    	ZeroMemory(&cbd, sizeof(cbd));
    	cbd.Usage = D3D11_USAGE_DYNAMIC;//常量缓冲区大多数需要频繁更新,因此后续都将主要使用DYNAMIC更新
    	cbd.ByteWidth = sizeof(ConstantBuffer); //上面创建的结构体的大小
    	cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    	cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    	// 新建常量缓冲区,不用初始数据,又没有顶点等等什么的
    	HR(m_pd3dDevice->CreateBuffer(&cbd, nullptr, m_pConstantBuffer.GetAddressOf()));
    
  • 不过,常量缓冲区ConstantBuffer里面的值还是要初始化一下的

  •     // 初始化常量缓冲区的值
    	// 如果你不熟悉这些矩阵,可以先忽略,待读完第四章后再回头尝试修改
    	m_CBuffer.world = XMMatrixIdentity();	// 单位矩阵的转置是它本身(Identity 本体)
    	m_CBuffer.view = XMMatrixTranspose(XMMatrixLookAtLH(
    		XMVectorSet(0.0f, 0.0f, -5.0f, 0.0f),
    		XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f),
    		XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f)
    	)); //观察矩阵 :XMMatrixLookAtLH(摄影机坐标,摄影机焦点坐标,摄影机上朝向坐标)
            // XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f) 设置每个分量并获取一个矩阵
    	    
    	m_CBuffer.proj = XMMatrixTranspose(XMMatrixPerspectiveFovLH(XM_PIDIV2, AspectRatio(), 1.0f, 1000.0f));
    	//XMMatrixTranspose 转置
    	//透视投影矩阵:XMMatrixPerspectiveFovLH(中心垂直弧度,宽高比,近平面距离,远平面距离)
    
        // float D3DApp::AspectRatio()const{
        // return static_cast<float>(m_ClientWidth) / m_ClientHeight;}
    
    
  • 现在就可以将常量缓冲区绑定到顶点着色器了吧,不急不急,我不是说要频繁更新吗?来看代码实现

  • 1、更新数据,你看你想怎么更新

  • void GameApp::UpdateScene(float dt){
    	//让立方体同时绕X轴和Y轴旋转,修改世界矩阵即可
    	static float phi = 0.0f, theta = 0.0f;
    	phi += 0.0001f, theta += 0.00015f;  //旋转速度
    	//XMMatrixRotationY  XMMatrixRotationX  XMMatrixRotationZ  按什么轴旋转
    	//XMMatrixTranspose 转置:顺时针旋转    不加上就相反旋转 
    	m_CBuffer.world = XMMatrixTranspose(XMMatrixRotationY(phi) * XMMatrixRotationY(theta));
    
    
  • 2、这里更新了,那常量缓冲区怎么知道?

  • D3D11_MAP枚举值类型的成员如下:

    D3D11_MAP成员含义
    D3D11_MAP_READ映射到内存的资源用于读取。该资源在创建的时候必须绑定了 D3D11_CPU_ACCESS_READ标签
    D3D11_MAP_WRITE映射到内存的资源用于写入。该资源在创建的时候必须绑定了 D3D11_CPU_ACCESS_WRITE标签
    D3D11_MAP_READ_WRITE映射到内存的资源用于读写。该资源在创建的时候必须绑定了 D3D11_CPU_ACCESS_READ和D3D11_CPU_ACCESS_WRITE标签
    D3D11_MAP_WRITE_DISCARD映射到内存的资源用于写入,之前的资源数据将会被抛弃。该 资源在创建的时候必须绑定了D3D11_CPU_ACCESS_WRITE和 D3D11_USAGE_DYNAMIC标签
    D3D11_MAP_WRITE_NO_OVERWRITE映射到内存的资源用于写入,但不能复写已经存在的资源。 该枚举值只能用于顶点/索引缓冲区。该资源在创建的时候需要 有D3D11_CPU_ACCESS_WRITE标签,在Direct3D 11不能用于 设置了D3D11_BIND_CONSTANT_BUFFER标签的资源,但在 11.1后可以。具体可以查阅MSDN文档
  • // 更新常量缓冲区,让立方体转起来
    	D3D11_MAPPED_SUBRESOURCE mappedData;
    	//map获取指向缓冲区中数据的指针并拒绝GPU对该缓冲区的访问
    	//([In]包含ID3D11Resource接口的资源对象,
    //     [In]缓冲区资源填0,
    //     [In]D3D11_MAP枚举值,指定读写相关操作,
    //     [In]填0,CPU需要等待GPU使用完毕当前缓冲区,
    //     [Out]获取到的已经映射到缓冲区的内存)
    	HR(m_pd3dImmediateContext->Map(m_pConstantBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData));
    
    
    //&mappedData和下面的联系
    
    	//安全内存复制
    //	errno_t memcpy_s(
    //		void* dest,          //目的内存地址
    //		size_t destMaxLen,   //目的内存最大长度
    //		const void* src,     //源内存地址
    //		size_t count);       //要拷贝的长度
    
    	//映射出来的内存我们可以通过memcpy_s函数来更新
    	//将数据从系统内存复制到常量缓冲区,即是上面更改的m_CBuffer的内容复制给已经映射到缓冲区的内存
    	//这样才实现了更改,然后执行这次常量缓冲区的内容
    	memcpy_s(mappedData.pData, sizeof(m_CBuffer), &m_CBuffer, sizeof(m_CBuffer));
    
    
    	//完成一个常量缓冲区时,我们应该在释放内存之前取消映射它(当然这里不是指针)
    	//让指向资源的指针无效并重新启用GPU对该资源的访问权限
    	//([In]包含ID3D11Resource接口的资源对象,[In]缓冲区资源填0)
    	m_pd3dImmediateContext->Unmap(m_pConstantBuffer.Get(), 0);
    
  • 万事俱备了,现在就可以将常量缓冲区绑定到顶点着色器了

  • // 将更新好的常量缓冲区绑定到顶点着色器
    	//([In]放入缓冲区的起始索引,例如上面指定了b0,则这里应为0,[In]设置的缓冲区数目,[In]用于设置的缓冲区数组)
    	m_pd3dImmediateContext->VSSetConstantBuffers(0, 1, m_pConstantBuffer.GetAddressOf());
    
三,HLSL代码实现
//Cube.hlsli

//cbuffer 用于声明一个常量缓冲区
//register(b0) 指的是该常量缓冲区位于寄存器索引为0的缓冲区
cbuffer ConstantBuffer : register(b0)
{
    matrix g_World; // matrix可以用float4x4替代。不加row_major的情况下,矩阵默认为列主矩阵,
    matrix g_View;  // 可以在前面添加row_major表示行主矩阵
    matrix g_Proj;  // 该教程往后将使用默认的列主矩阵,但需要在C++代码端预先将矩阵进行转置。
}



struct VertexIn
{
    float3 posL : POSITION;
    float4 color : COLOR;
};

struct VertexOut
{
    float4 posH : SV_POSITION;
    float4 color : COLOR;
};


// Cube_VS.hlsl
#include "Cube.hlsli"

VertexOut VS(VertexIn vIn)
{
 // mul 才是矩阵乘法, 运算符*要求操作对象为行列数相等的两个矩阵,结果为Cij = Aij * Bij
    vOut.posH = mul(float4(vIn.posL, 1.0f), gWorld); //外面修改的不就是世界矩阵吗,世界转
    vOut.posH = mul(vOut.posH, gView);               
    vOut.posH = mul(vOut.posH, gProj);               
    vOut.color = vIn.color;                         // 这里alpha通道的值默认为1.0
    return vOut;
}
 //变换矩阵是通过与顶点坐标(或向量)进行矩阵乘法来实现对顶点的变换,变换得到的顶点坐标(或向量)为在原坐标系下对应的坐标。

// Cube_PS.hlsl
#include "Cube.hlsli"

float4 PS(VertexOut pIn) : SV_Target
{
    return pIn.color;  
}
四、鼠键实现

1、这个我还是非常非常推荐看X_jun师兄的,我写的话,可能就完全复制粘贴了…

键盘和鼠标输入


作业相关代码&代码分析
  • 这里就简单这次作业的代码分析一下
  • 一、顶点设置
// ******************
	// 设置梯台顶点
	//    5________ 6
	//    /|      /\
	//   /_|_____/  \
	//  1|4|_ _ 2\_ _\7
	//   | /      \  /
	//   |/______ _\/
	//  0           3
	VertexPosColor vertices[] =
	{
		{ XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f) },//0
		{ XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },//1
		{ XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },//2
		{ XMFLOAT3(2.0f, -1.0f, -1.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },//3
		{ XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },//4
		{ XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT4(1.0f, 0.0f, 1.0f, 1.0f) },//5
		{ XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f) },//6
		{ XMFLOAT3(2.0f, -1.0f, 1.0f), XMFLOAT4(0.0f, 1.0f, 1.0f, 1.0f) }//7
	};
  • 二、索引数组
// 索引数组
	// 使用索引缓冲区进行替代指定顺序绘制,可以有效减少顶点缓冲区的占用空间,避免提供大量重复的顶点数据
	DWORD indices[] = {
		// 正面
		0, 1, 2,
		2, 3, 0,
		// 左面
		4, 5, 1,
		1, 0, 4,
		// 顶面
		1, 5, 6,
		6, 2, 1,
		// 背面
		7, 6, 5,
		5, 4, 7,
		// 右面
		3, 2, 6,
		6, 7, 3,
		// 底面
		4, 0, 3,
		3, 7, 4
	};
	// 设置索引缓冲区描述
	D3D11_BUFFER_DESC ibd;
	ZeroMemory(&ibd, sizeof(ibd));
	ibd.Usage = D3D11_USAGE_IMMUTABLE;
	ibd.ByteWidth = sizeof indices;
	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	ibd.CPUAccessFlags = 0;
	// 新建索引缓冲区
	InitData.pSysMem = indices;//初始化数据
	HR(m_pd3dDevice->CreateBuffer(&ibd, &InitData, m_pIndexBuffer.GetAddressOf()));

	// 输入装配阶段的索引缓冲区设置
	// ([In]索引缓冲区,[In]数据格式,[In]字节偏移量)
	// DXGI_FORMAT_R32_UINT	 4(字节)	0-2147483647(索引范围)
	m_pd3dImmediateContext->IASetIndexBuffer(m_pIndexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0);

  • 三、让立方体旋转&代码分析
//让立方体同时绕X轴和Y轴旋转,修改世界矩阵即可
	static float phi = 0.0f, theta = 0.0f;
	phi += 0.0001f, theta += 0.00015f;
	m_CBuffer.world = XMMatrixTranspose(XMMatrixRotationX(phi) * XMMatrixRotationY(theta));
	// 更新常量缓冲区,让立方体转起来
	D3D11_MAPPED_SUBRESOURCE mappedData;
	//map获取指向缓冲区中数据的指针并拒绝GPU对该缓冲区的访问
	//([In]包含ID3D11Resource接口的资源对象,[In]缓冲区资源填0,[In]D3D11_MAP枚举值,指定读写相关操作
	// [In]填0,CPU需要等待GPU使用完毕当前缓冲区,[Out]获取到的已经映射到缓冲区的内存)
	HR(m_pd3dImmediateContext->Map(m_pConstantBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData));

	//映射出来的内存我们可以通过memcpy_s函数来更新
	memcpy_s(mappedData.pData, sizeof(m_CBuffer), &m_CBuffer, sizeof(m_CBuffer));
	//让指向资源的指针无效并重新启用GPU对该资源的访问权限
	//([In]包含ID3D11Resource接口的资源对象,[In]缓冲区资源填0)
	m_pd3dImmediateContext->Unmap(m_pConstantBuffer.Get(), 0);
  • 四、旋转立方体实现原理
//cbuffer 用于声明一个常量缓冲区
//register(b0) 指的是该常量缓冲区位于寄存器索引为0的缓冲区
cbuffer ConstantBuffer : register(b0)
{
    matrix g_World; // matrix可以用float4x4替代。不加row_major的情况下,矩阵默认为列主矩阵,
    matrix g_View;  // 可以在前面添加row_major表示行主矩阵
    matrix g_Proj;  // 该教程往后将使用默认的列主矩阵,但需要在C++代码端预先将矩阵进行转置。
}


VertexOut VS(VertexIn vIn)
{
    VertexOut vOut;
    vOut.posH = mul(float4(vIn.posL, 1.0f), g_World);  // mul 才是矩阵乘法, 运算符*要求操作对象为
    vOut.posH = mul(vOut.posH, g_View);               // 行列数相等的两个矩阵,结果为
    vOut.posH = mul(vOut.posH, g_Proj);               // Cij = Aij * Bij
    vOut.color = vIn.color;                         // 这里alpha通道的值默认为1.0
    return vOut;
}
  • 五、鼠标,键盘状态
static float cubePhi = 0.0f, cubeTheta = 0.0f;
	// 获取鼠标状态
	Mouse::State mouseState = m_pMouse->GetState();//获取当前帧下鼠标的运动状态
	Mouse::State lastMouseState = m_MouseTracker.GetLastState();//返回lastState绝对坐标x或相对偏移量
	// 获取键盘状态
	Keyboard::State keyState = m_pKeyboard->GetState();
	Keyboard::State lastKeyState = m_KeyboardTracker.GetLastState();

	// 更新鼠标按钮状态跟踪器,仅当鼠标按住的情况下才进行移动
	m_MouseTracker.Update(mouseState);
	m_KeyboardTracker.Update(keyState);
	if (mouseState.leftButton == true && m_MouseTracker.leftButton == m_MouseTracker.HELD)
	{
		cubeTheta -= (mouseState.x - lastMouseState.x) * 0.01f;//获取两帧之间的鼠标x相对位移量
		cubePhi -= (mouseState.y - lastMouseState.y) * 0.01f;//获取两帧之间的鼠标y相对位移量
	}
	if (keyState.IsKeyDown(Keyboard::W))
		cubePhi += dt * 2;
	if (keyState.IsKeyDown(Keyboard::S))
		cubePhi -= dt * 2;
	if (keyState.IsKeyDown(Keyboard::A))
		cubeTheta += dt * 2;
	if (keyState.IsKeyDown(Keyboard::D))
		cubeTheta -= dt * 2;

请添加图片描述


思考&小结
  • 还是这么说吧,对图形渲染这学习呢,从刚刚开始的完全看不懂到现在已经慢慢了解,可以来说是一个很大的进步了,我也是好久没有这种感觉了,什么感觉呢?是那种对一样东西从刚刚开始去接触,一头雾水,完全看不见往什么地方走,想放弃。到后面慢慢去学,然后豁然开朗的感觉。果然呀,船到桥头自然直!
  • 现在呢我还是按xjun师兄的博客去学习,一个一个的去看,不会的或者博客中没有解释的就上百度去搜索,找到答案,然后记录在代码上面。对于代码呢,我也是去看每一个代码,去找代码的实现原理,然后注释,便于以后回头去看,也同样让自己能学的更好。可能有时候,有些比较复杂或者困难的代码,不得不去暂时放弃一下,这应该往后会渐渐理解的。
  • 当然我还是有一个问题的,对于师兄给的作业呢?应该师兄是想要我们去了解如何实现吧,如果单单去做出来,直接利用xjun师兄的代码框架,那应该就很简单了(应该吧)。
  • 对于下周的计划呢,像我在大组作业中的一样,对图形渲染的学习,还是看xjun师兄的博客,第二段那样去学习,同时学习c++,加强一下。
  • 不过我现在不是很知道其他人学习是什么情况,可能别人都是非常厉害,感觉我是不是慢了。嗯…对未知的恐惧,慢慢有点担心了。算了,还是看一步走一步吧,努力过就行,没有遗憾就好。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值