本篇是
D3D中的纹理贴图(1)的后续篇,编写了一个例子来演示同时使用纹理,光照和材质进行渲染。
需要在工程中设置链接d3dx9.lib d3d9.lib dxguid.lib
dinput8.lib winmm.lib。
由于文件中用到了GE_APP和GE_INPUT这两个类,它们的具体使用说明请参阅
主窗口和DirectInput的封装。
还用到了GE_TIMER类,具体说明请参阅游戏中时间的封装。
材质和光照的具体内容请参阅
D3D中的材质和光照处理 。
若发现代码中存在错误,敬请指出。
源码及素材下载
来看看头文件TextureCone.h的定义:
/*************************************************************************************
[Include File]
PURPOSE:
Define for material, light, texture mapped.
*************************************************************************************/
#ifndef TEXTURE_CONE_H
#define TEXTURE_CONE_H
////////////////////////////////////////// TYPES AND MACROS //////////////////////////////////////////
#define TIGER 0
#define CHESS 1
#define WALL 2
// define for cone
struct CONE_CUSTOM_VERTEX
{
float x, y, z;
float nx, ny, nz;
float u, v;
};
#define CONE_CUSTOM_VERTEX_FVF (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
// define for square
struct SQUARE_CUSTOM_VERTEX
{
float x, y, z, rhw;
float u, v;
};
#define SQUARE_CUSTOM_VERTEX_FVF (D3DFVF_XYZRHW | D3DFVF_TEX1)
////////////////////////////////////////// CLASS //////////////////////////////////////////
class TEXTURE_CONE
{
IDirect3D9* _d3d;
IDirect3DDevice9* _d3d_device;
IDirect3DVertexBuffer9* _cone_vertex_buffer;
IDirect3DVertexBuffer9* _square_vertex_buffer;
IDirect3DTexture9* _tiger_texture;
IDirect3DTexture9* _chess_texture;
IDirect3DTexture9* _wall_texture;
public:
TEXTURE_CONE();
~TEXTURE_CONE();
bool Create_D3D_Device(HWND hwnd, bool full_screen = true);
bool Init_Cone_Vertex_Buffer();
bool Init_Square_Vertex_Buffer();
void Set_Camera();
void Set_Point_Light(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe, D3DVECTOR& pos);
void Set_Object_Material(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe,
D3DCOLORVALUE& emi, float power);
void Compute_Triangle_Normal(D3DXVECTOR3& v1, D3DXVECTOR3& v2, D3DXVECTOR3& v3, D3DVECTOR& normal);
bool Create_All_Texture();
void Set_Texture(int choice);
void Render();
void Release_COM_Object();
};
#endif
再来看看它的实现,即TextureCone.cpp的定义:
/*************************************************************************************
[Implement File]
PURPOSE:
Define for material, light, texture mapped.
*************************************************************************************/
#include "GE_COMMON.h"
#include "TextureCone.h"
//------------------------------------------------------------------------------------
// Constructor, initialize all pointer with NULL.
//------------------------------------------------------------------------------------
TEXTURE_CONE::TEXTURE_CONE()
{
_d3d = NULL;
_d3d_device = NULL;
_cone_vertex_buffer = NULL;
_square_vertex_buffer = NULL;
_tiger_texture = NULL;
_chess_texture = NULL;
_wall_texture = NULL;
}
//------------------------------------------------------------------------------------
// Destructor, release resource allocated for COM object.
//------------------------------------------------------------------------------------
TEXTURE_CONE::~TEXTURE_CONE()
{
Release_COM_Object();
}
//------------------------------------------------------------------------------------
// Create direct3D interface and direct3D device.
//------------------------------------------------------------------------------------
bool TEXTURE_CONE::Create_D3D_Device(HWND hwnd, bool full_screen)
{
// Create a IDirect3D9 object and returns an interace to it.
_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if(_d3d == NULL)
return false;
// retrieve adapter capability
D3DCAPS9 d3d_caps;
_d3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3d_caps);
bool hardware_process_enable = (d3d_caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ? true : false);
// Retrieves the current display mode of the adapter.
D3DDISPLAYMODE display_mode;
if(FAILED(_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &display_mode)))
return false;
// set present parameter for direct3D device
D3DPRESENT_PARAMETERS present_param = {0};
present_param.BackBufferWidth = WINDOW_WIDTH;
present_param.BackBufferHeight = WINDOW_HEIGHT;
present_param.BackBufferFormat = display_mode.Format;
present_param.BackBufferCount = 1;
present_param.hDeviceWindow = hwnd;
present_param.Windowed = !full_screen;
present_param.SwapEffect = D3DSWAPEFFECT_FLIP;
present_param.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
// Creates a device to represent the display adapter.
DWORD behavior_flags;
behavior_flags = hardware_process_enable ?
D3DCREATE_HARDWARE_VERTEXPROCESSING : D3DCREATE_SOFTWARE_VERTEXPROCESSING;
if(FAILED(_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, behavior_flags,
&present_param, &_d3d_device)))
{
return false;
}
// create successfully
return true;
}
//------------------------------------------------------------------------------------
// Initialize vertex buffer for cone.
//------------------------------------------------------------------------------------
bool TEXTURE_CONE::Init_Cone_Vertex_Buffer()
{
CONE_CUSTOM_VERTEX cone_custom_vertex[12];
D3DXVECTOR3 v[] =
{
D3DXVECTOR3(5.0f, 6.0f, 5.0f), // left triangle
D3DXVECTOR3(6.0f, 0.0f, 3.0f),
D3DXVECTOR3(1.0f, 0.0f, 7.0f),
D3DXVECTOR3(5.0f, 6.0f, 5.0f), // right triangle
D3DXVECTOR3(10.0f, 0.0f, 8.0f),
D3DXVECTOR3(6.0f, 0.0f, 3.0f),
D3DXVECTOR3(5.0f, 6.0f, 5.0f), // back triangle
D3DXVECTOR3(1.0f, 0.0f, 7.0f),
D3DXVECTOR3(10.0f, 0.0f, 8.0f),
D3DXVECTOR3(1.0f, 0.0f, 7.0f), // bottom triangle
D3DXVECTOR3(6.0f, 0.0f, 3.0f),
D3DXVECTOR3(10.0f, 0.0f, 8.0f)
};
D3DVECTOR normal;
// compute all triangle normal
for(int i = 0; i < 12; i += 3)
{
// compute current triangle's normal
Compute_Triangle_Normal(v[i], v[i+1], v[i+2], normal);
// assign current vertex coordinate and current triangle normal to custom vertex array
for(int j = 0; j < 3; j++)
{
int k = i + j;
cone_custom_vertex[k].x = v[k].x;
cone_custom_vertex[k].y = v[k].y;
cone_custom_vertex[k].z = v[k].z;
cone_custom_vertex[k].nx = normal.x;
cone_custom_vertex[k].ny = normal.y;
cone_custom_vertex[k].nz = normal.z;
}
}
cone_custom_vertex[0].u = 0.0f;
cone_custom_vertex[0].v = 0.0f;
cone_custom_vertex[1].u = 0.0f;
cone_custom_vertex[1].v = 1.0f;
cone_custom_vertex[2].u = 1.0f;
cone_custom_vertex[2].v = 1.0f;
cone_custom_vertex[3].u = 0.0f;
cone_custom_vertex[3].v = 0.0f;
cone_custom_vertex[4].u = 1.0f;
cone_custom_vertex[4].v = 0.0f;
cone_custom_vertex[5].u = 1.0f;
cone_custom_vertex[5].v = 1.0f;
cone_custom_vertex[6].u = 0.0f;
cone_custom_vertex[6].v = 0.0f;
cone_custom_vertex[7].u = 1.0f;
cone_custom_vertex[7].v = 0.0f;
cone_custom_vertex[8].u = 1.0f;
cone_custom_vertex[8].v = 1.0f;
cone_custom_vertex[9].u = 0.0f;
cone_custom_vertex[9].v = 0.0f;
cone_custom_vertex[10].u = 1.0f;
cone_custom_vertex[10].v = 0.0f;
cone_custom_vertex[11].u = 1.0f;
cone_custom_vertex[11].v = 1.0f;
BYTE* vertex_data;
// create vertex buffer
if(FAILED(_d3d_device->CreateVertexBuffer(12 * sizeof(CONE_CUSTOM_VERTEX), 0, CONE_CUSTOM_VERTEX_FVF,
D3DPOOL_DEFAULT, &_cone_vertex_buffer, NULL)))
{
return false;
}
// get data pointer to vertex buffer
if(FAILED(_cone_vertex_buffer->Lock(0, 0, (void **) &vertex_data, 0)))
return false;
// copy custom vertex data into vertex buffer
memcpy(vertex_data, cone_custom_vertex, sizeof(cone_custom_vertex));
// unlock vertex buffer
_cone_vertex_buffer->Unlock();
return true;
}
//------------------------------------------------------------------------------------
// Initialize vertex buffer for square.
//------------------------------------------------------------------------------------
bool TEXTURE_CONE::Init_Square_Vertex_Buffer()
{
SQUARE_CUSTOM_VERTEX square_custom_vertex[] =
{
{0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
{130.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f},
{0.0f, 130.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{130.0f, 130.0f, 0.0f, 1.0f, 1.0f, 1.0f}
};
BYTE* vertex_data;
// create vertex buffer
if(FAILED(_d3d_device->CreateVertexBuffer(12 * sizeof(SQUARE_CUSTOM_VERTEX), 0, SQUARE_CUSTOM_VERTEX_FVF,
D3DPOOL_MANAGED, &_square_vertex_buffer, NULL)))
{
return false;
}
// get data pointer to vertex buffer
if(FAILED(_square_vertex_buffer->Lock(0, 0, (void **) &vertex_data, 0)))
return false;
// copy custom vertex data into vertex buffer
memcpy(vertex_data, square_custom_vertex, sizeof(square_custom_vertex));
// unlock vertex buffer
_square_vertex_buffer->Unlock();
return true;
}
//------------------------------------------------------------------------------------
// Set camera position.
//------------------------------------------------------------------------------------
void TEXTURE_CONE::Set_Camera()
{
D3DXVECTOR3 eye(-6.0, 1.5, 10.0);
D3DXVECTOR3 at(6.0, 2.0, 3.0);
D3DXVECTOR3 up(0.0, 1.0, 0.0);
D3DXMATRIX view_matrix;
// Builds a left-handed, look-at matrix.
D3DXMatrixLookAtLH(&view_matrix, &eye, &at, &up);
// Sets d3d device view transformation state.
_d3d_device->SetTransform(D3DTS_VIEW, &view_matrix);
D3DXMATRIX proj_matrix;
// Builds a left-handed perspective projection matrix based on a field of view.
D3DXMatrixPerspectiveFovLH(&proj_matrix, D3DX_PI/2, WINDOW_WIDTH / WINDOW_HEIGHT, 1.0, 1000.0);
// Sets d3d device projection transformation state.
_d3d_device->SetTransform(D3DTS_PROJECTION, &proj_matrix);
// enable automatic normalization of vertex normals
_d3d_device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
}
//------------------------------------------------------------------------------------
// Set point light.
//------------------------------------------------------------------------------------
void TEXTURE_CONE::Set_Point_Light(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe, D3DVECTOR& pos)
{
D3DLIGHT9 light;
// clear memory with 0
ZeroMemory(&light, sizeof(D3DLIGHT9));
light.Type = D3DLIGHT_POINT;
light.Diffuse = dif;
light.Ambient = amb;
light.Specular = spe;
light.Position = pos;
light.Attenuation0 = 1.0;
light.Attenuation1 = 0.0;
light.Attenuation2 = 0.0;
light.Range = 1000.0;
// Assigns point lighting properties for this device
_d3d_device->SetLight(0, &light);
// enable point light
_d3d_device->LightEnable(0, TRUE);
// enable light
_d3d_device->SetRenderState(D3DRS_LIGHTING, TRUE);
// add ambient light
_d3d_device->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(50, 50, 50));
}
//------------------------------------------------------------------------------------
// Sets the material properties for the device.
//------------------------------------------------------------------------------------
void TEXTURE_CONE::Set_Object_Material(D3DCOLORVALUE& dif, D3DCOLORVALUE& amb, D3DCOLORVALUE& spe,
D3DCOLORVALUE& emi, float power)
{
D3DMATERIAL9 material;
material.Diffuse = dif;
material.Ambient = amb;
material.Specular = spe;
material.Emissive = emi;
material.Power = power;
// Sets the material properties for the device.
_d3d_device->SetMaterial(&material);
}
//------------------------------------------------------------------------------------
// Create all texture interface.
//------------------------------------------------------------------------------------
bool TEXTURE_CONE::Create_All_Texture()
{
if(FAILED( D3DXCreateTextureFromFile(_d3d_device, "tiger.jpg", &_tiger_texture) ))
goto fail;
if(FAILED( D3DXCreateTextureFromFile(_d3d_device, "chess.bmp", &_chess_texture) ))
goto fail;
if(FAILED( D3DXCreateTextureFromFile(_d3d_device, "wall.bmp", &_wall_texture) ))
goto fail;
return true;
fail:
MessageBox(NULL, "Create texture interface failed.", "ERROR", MB_OK);
return false;
}
//------------------------------------------------------------------------------------
// Set texture interface to DirectX3D.
//------------------------------------------------------------------------------------
void TEXTURE_CONE::Set_Texture(int choice)
{
switch(choice)
{
case TIGER:
_d3d_device->SetTexture(0, _tiger_texture);
break;
case CHESS:
_d3d_device->SetTexture(0, _chess_texture);
break;
case WALL:
_d3d_device->SetTexture(0, _wall_texture);
break;
}
}
//------------------------------------------------------------------------------------
// Draw cone and square.
//------------------------------------------------------------------------------------
void TEXTURE_CONE::Render()
{
if(_d3d_device == NULL)
return;
// clear surface with black
_d3d_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0);
// begin scene
_d3d_device->BeginScene();
// 1) draw cone with texture stick on surface
// Binds a vertex buffer to a device data stream.
_d3d_device->SetStreamSource(0, _cone_vertex_buffer, 0, sizeof(CONE_CUSTOM_VERTEX));
// Sets the current vertex stream declaration.
_d3d_device->SetFVF(CONE_CUSTOM_VERTEX_FVF);
// Sets the sampler state value.
// D3DSAMP_MIPFILTER:
// Mipmap filter to use during minification.
//
// D3DTEXF_POINT:
// Point filtering used as a texture magnification or minification filter. The texel with coordinates
// nearest to the desired pixel value is used. The texture filter to be used between mipmap levels
// is nearest-point mipmap filtering. The rasterizer uses the color from the texel of the nearest
// mipmap texture.
_d3d_device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
// D3DSAMP_MAGFILTER:
// Magnification filter of type D3DTEXTUREFILTERTYPE.
//
// D3DTEXF_LINEAR:
// Bilinear interpolation filtering used as a texture magnification or minification filter.
// A weighted average of a 2 × 2 area of texels surrounding the desired pixel is used.
// The texture filter to use between mipmap levels is trilinear mipmap interpolation.
// The rasterizer linearly interpolates pixel color, using the texels of the two nearest mipmap textures.
_d3d_device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// D3DSAMP_MINFILTER:
// Minification filter of type D3DTEXTUREFILTERTYPE.
_d3d_device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
// Renders a sequence of nonindexed, geometric primitives of the specified type from the current
// set of data input streams.
_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);
// 2) draw square with texture stick on surface
// Binds a vertex buffer to a device data stream.
_d3d_device->SetStreamSource(0, _square_vertex_buffer, 0, sizeof(SQUARE_CUSTOM_VERTEX));
// Sets the current vertex stream declaration.
_d3d_device->SetFVF(SQUARE_CUSTOM_VERTEX_FVF);
// draw square
_d3d_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
// end scene
_d3d_device->EndScene();
// Presents the contents of the next buffer in the sequence of back buffers owned by the device.
_d3d_device->Present(NULL, NULL, NULL, NULL);
}
//------------------------------------------------------------------------------------
// Compute triangle normal.
//------------------------------------------------------------------------------------
void TEXTURE_CONE::Compute_Triangle_Normal(D3DXVECTOR3& v1, D3DXVECTOR3& v2, D3DXVECTOR3& v3, D3DVECTOR& normal)
{
D3DXVECTOR3 vec1 = v1 - v2;
D3DXVECTOR3 vec2 = v1 - v3;
D3DXVECTOR3 normal_vec;
D3DXVec3Cross(&normal_vec, &vec1, &vec2);
D3DXVec3Normalize(&normal_vec, &normal_vec);
normal = (D3DVECTOR) normal_vec;
}
//------------------------------------------------------------------------------------
// Release all resource allocated for D3D.
//------------------------------------------------------------------------------------
void TEXTURE_CONE::Release_COM_Object()
{
Safe_Release(_tiger_texture);
Safe_Release(_chess_texture);
Safe_Release(_wall_texture);
Safe_Release(_square_vertex_buffer);
Safe_Release(_cone_vertex_buffer);
Safe_Release(_d3d_device);
Safe_Release(_d3d);
}
这里主要指出Render()函数需要注意的一个细节,在绘制三棱锥时的代码:
_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);
而在绘制正方形时的代码是:
_d3d_device- >DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
一个是D3DPT_TRIANGLELIST,另一个却是D3DPT_TRIANGLESTRIP,这是因为他们存储顶点的方式不同,来看看SDK文档提供的说明:
D3DPT_TRIANGLELIST
Renders the specified vertices as a sequence of isolated triangles. Each
group of three vertices defines a separate triangle.
Back-face culling is affected by the current winding-order render state.
D3DPT_TRIANGLESTRIP
Renders the vertices as a triangle strip. The backface-culling flag is
automatically flipped on even-numbered triangles.
三棱锥数组存储了12个顶点,每个三角形面有3个顶点,所以12个顶点共存储了4个三角形面的顶点,这些顶点部分重复了,所以使用
D3DPT_TRIANGLELIST模式来绘制。
CONE_CUSTOM_VERTEX cone_custom_vertex[12];
而正方形仅仅保存了4个顶点的数据,顶点存储没有冗余,顶点被一些面共用,所以使用D3DPT_TRIANGLESTRIP来渲染。
SQUARE_CUSTOM_VERTEX square_custom_vertex[] =
{
{0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
{130.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f},
{0.0f, 130.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{130.0f, 130.0f, 0.0f, 1.0f, 1.0f, 1.0f}
};
再来看看测试代码main.cpp:
/*************************************************************************************
[Implement File]
PURPOSE:
Test for texture, material, lighting.
*************************************************************************************/
#define DIRECTINPUT_VERSION 0x0800
#include "GE_COMMON.h"
#include "GE_APP.h"
#include "GE_INPUT.h"
#include "GE_TIMER.h"
#include "TextureCone.h"
#pragma warning(disable : 4305 4996)
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
GE_APP ge_app;
GE_INPUT ge_input;
GE_TIMER ge_timer;
TEXTURE_CONE texture_cone;
MSG msg = {0};
// define property for material
D3DCOLORVALUE material_dif = {1.0f, 1.0f, 1.0f, 1.0f};
D3DCOLORVALUE material_amb = {1.0f, 1.0f, 1.0f, 1.0f};
D3DCOLORVALUE material_spe = {1.0f, 0.0f, 0.0f, 1.0f};
D3DCOLORVALUE material_emi = {0.0f, 0.0f, 1.0f, 1.0f};
// define property for light
D3DCOLORVALUE light_dif = {1.0f, 0.0f, 0.0f, 1.0f};
D3DCOLORVALUE light_amb = {0.0f, 0.7f, 0.0f, 1.0f};
D3DCOLORVALUE light_spe = {0.0f, 0.0f, 0.0f, 0.0f};
D3DVECTOR light_pos = {5.0f, 6.0f, -20.0f};
float last_render_time = 0;
// create window
if(! ge_app.Create_Window("Material and light test", instance, cmd_show))
return false;
HWND hwnd = ge_app.Get_Window_Handle();
// create directinput
ge_input.Create_Input(instance, hwnd);
SetWindowPos(hwnd, 0, 0,0,0,0, SWP_NOSIZE);
SetCursorPos(0, 0);
// initialize game time
ge_timer.Init_Game_Time();
// Create direct3D interface and direct3D device.
if(! texture_cone.Create_D3D_Device(hwnd, false))
return false;
// Initialize cone vertex buffer with curstom vertex structure.
if(! texture_cone.Init_Cone_Vertex_Buffer())
return false;
// Initialize square vertex buffer with curstom vertex structure.
if(! texture_cone.Init_Square_Vertex_Buffer())
return false;
// Create all texture interface.
texture_cone.Create_All_Texture();
// Set camera position.
texture_cone.Set_Camera();
// Sets the material properties for the device.
texture_cone.Set_Object_Material(material_dif, material_amb, material_spe, material_emi, 0);
// Set point light.
texture_cone.Set_Point_Light(light_dif, light_amb, light_spe, light_pos);
// Set texture interface to tiger
texture_cone.Set_Texture(TIGER);
// Draw cone and square.
texture_cone.Render();
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0,0 , PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// read data from keyboard buffer
if(ge_input.Read_Keyboard())
{
// press "ESC", close window.
if(ge_input.Is_Key_Pressed(DIK_ESCAPE))
PostQuitMessage(0);
bool key_left_pressed = ge_input.Is_Key_Pressed(DIK_LEFT);
bool key_right_pressed = ge_input.Is_Key_Pressed(DIK_RIGHT);
bool key_up_pressed = ge_input.Is_Key_Pressed(DIK_UP);
// set texture address mode
if(key_left_pressed || key_right_pressed || key_up_pressed)
{
// Set texture interface to DirectX3D.
if(key_left_pressed)
texture_cone.Set_Texture(TIGER);
if(key_right_pressed)
texture_cone.Set_Texture(CHESS);
if(key_up_pressed)
texture_cone.Set_Texture(WALL);
// draw cone and square
texture_cone.Render();
}
}
// if it is time to reset light
if((ge_timer.Get_Game_Play_Time() - last_render_time) > 30)
{
if(light_amb.r < 1.0f)
light_amb.r += 0.001f;
else
light_amb.r = 0.0f;
if(light_amb.g < 1.0f)
light_amb.g += 0.02f;
else
light_amb.g = 0.0f;
if(light_amb.b < 1.0f)
light_amb.b += 0.03f;
else
light_amb.b = 0.0f;
texture_cone.Set_Point_Light(light_dif, light_amb, light_spe, light_pos);
texture_cone.Render();
// update last render time
last_render_time = ge_timer.Get_Game_Play_Time();
}
}
}
UnregisterClass(WINDOW_CLASS_NAME, instance);
return true;
}
用左右上三个方向键控制使用不同的纹理图渲染。
运行效果: