|
меню : 1246 | Телеграмм GameEngine MirxenSoft
Ссылка на группу нhttps://t.me/+uFU53XJzoZpkMWMy
| |
| меню : 1244 | нашу команду энтузиастов нужны:
- Разработчики на Unreal Engine 5 - 3д художники по персонажам - Концепт художники - Level-дизайнеры - Гейм-дизайнер
О проекте. Линейно сюжетный зомби хоррор с элементами стелса, упор идёт исключительно на сюжет!
Наш проект в себе связывает Silent Hill, Resident Evil и Left 4 Dead!
В команде уже есть, VFX Дизайнера, Нарративщик, Cценарист, ЛВЛ Дизайнер, Геймдизайнер, 3д Художники по персонажам, в данный момент делаются но ищем ещё ребят, Аниматор, Маркетолог.
Питч проекта>>> https://docs.google.… fj08as9M/edit
Плюсы проекта: он легко реализуется, делаем из ассетов, которые будем заменять, это не ААА, это "Проект мечты". Игра рассчитана на 3ч 50 минут. За данное время мы расскажем историю и сюжет самого мира, что с ним происходило, что с ним происходит, и что будет происходить.
Что нужно будет делать.
Разработчикам на Unreal Engine 5:
- Реализация игровых механик и логики; - Знание blueprint и c++; - Опыт работы с git; - Профайлинг и оптимизация кода.
3д художникам по персонажам:
- Создавать High-poly Midl-poly - Заниматься скиннингом, риггингом и правкой готовых моделей; - Знание и умение создавать Game-ready модели.
Концепт художникам:
- Умение создавать красивые и информативные концепты; - Хорошее понимание принципов дизайна, композиции, теории цвета; - Насмотренность.
level-дизайнерам:
- Разрабатывать блокинг уровней; - Создавать готовые уровни на основе заявленных в ней точек интереса и концепт-артов; - Управлять вниманием игрока, продумывать маршруты передвижения, так-же добавить возможность к стелс передвижению, лазы, канализации и т.д.
Гейм-дизайнеру:
- Продумывание и описание игрового процесса и внутриигрового баланса; - Ведение и написание документации для каждого отдела - Составлять ТЗ для разных отделов: 3D, дизайн, разработка...
Мы предлагаем:
- Дружный коллектив; - Свободный от работы график а позже % с продаж игры.
| |
| меню : 1243 | https://qna.habr.com/q/628253
| |
| меню : 1242 | Собираю команду для разработки.
Всем привет! Я недавно начал изучать и unity, куча свободного времени, а самостоятельно учиться порой трудно и некоторое не понятно поэтому подумав я решил что мне нужен напарник или же наставник который готов уделять время изучению/помощи в изучении Unity. Так же планирую парочку проектов в которым ТЫ можешь поучаствовать вместе со мной. Возраст, пол, национальность не имеет значения!
Писать в личьку.
| |
|
меню : 1241 | https://rastertek.com/tutdx11win10.html
| |
| меню : 1240 | Normal mapping is a form of bump mapping that is used to produce a 3D effect using just two 2D textures. In normal mapping we use a special texture called a normal map which is essentially a look up table for surface normals. Each pixel in the normal map indicates the light direction for the corresponding pixel on the texture color map.
For example, take the following color map:
A normal map for the above texture would look like the following:
Using the normal map with the current light direction for each pixel would then produce the following normal mapped texture:
And if we apply that texture to something like a flat surface sphere, it will now appear to have a 3D surface instead of a flat one:
As you can see the effect is very realistic and the cost of producing it using normal mapping is far less expensive than rendering a high polygon surface to get the same result.
To create a normal map, you usually need someone to produce a 3D model of the surface and then use a tool to convert that 3D model into a normal map. There are also certain tools that will work with 2D textures to produce a somewhat decent normal map.
The tools that create normal maps take the x, y, z coordinates and translate them to red, green, blue pixels with the intensity of each color indicating the angle of the normal they represent. The normal of our polygon surface is still calculated the same way as before. However, the two other normals we need to calculate require the vertex and texture coordinates for that polygon surface. These two normals are called the tangent and binormal. The diagram below shows the direction of each normal:
The normal is still pointing straight out towards the viewer. The tangent and binormal however run across the surface of the polygon with the tangent going along the x-axis and the binormal going along the y-axis. These two normals then directly translate to the tu and tv texture coordinates of the normal map with the texture U coordinate mapping to the tangent and the texture V coordinate mapping to the binormal.
We will need to do some precalculation to determine the binormal and tangent vectors. You can do these calculations inside the shader, but it is fairly expensive with all the floating-point math involved. In this tutorial I will use a function in my C++ code so you can see for yourself and understand the math used in calculating these two extra normal vectors. Also, most 3D modeling tools will also export these normals as part of your model if they are selected. Once we have precalculated the tangent and binormal we can use this equation to determine the bump normal at any pixel using the normal map:
bumpNormal = (normalMap.x * input.tangent) + (normalMap.y * input.binormal) + (normalMap.z * input.normal); Once we have the normal for that pixel we can then calculate against the light direction and multiply by the color value of the pixel from the color texture to get our final result.
Framework
The frame work for this tutorial looks like the following:
We will start the tutorial by looking at the normal map HLSL shader code.
Normalmap.vs
//////////////////////////////////////////////////////////////////////////////// // Filename: normalmap.vs ////////////////////////////////////////////////////////////////////////////////
///////////// // GLOBALS // ///////////// cbuffer MatrixBuffer { matrix worldMatrix; matrix viewMatrix; matrix projectionMatrix; }; Both the VertexInputType and PixelInputType now have a tangent and binormal vector for normal map calculations.
////////////// // TYPEDEFS // ////////////// struct VertexInputType { float4 position : POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL; };
struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL; };
//////////////////////////////////////////////////////////////////////////////// // Vertex Shader //////////////////////////////////////////////////////////////////////////////// PixelInputType NormalMapVertexShader(VertexInputType input) { PixelInputType output;
// Change the position vector to be 4 units for proper matrix calculations. input.position.w = 1.0f;
// Calculate the position of the vertex against the world, view, and projection matrices. output.position = mul(input.position, worldMatrix); output.position = mul(output.position, viewMatrix); output.position = mul(output.position, projectionMatrix); // Store the texture coordinates for the pixel shader. output.tex = input.tex; // Calculate the normal vector against the world matrix only and then normalize the final value. output.normal = mul(input.normal, (float3x3)worldMatrix); output.normal = normalize(output.normal); Both the input tangent vector and binormal vector are calculated against the world matrix and then normalized the same way the input normal vector is.
// Calculate the tangent vector against the world matrix only and then normalize the final value. output.tangent = mul(input.tangent, (float3x3)worldMatrix); output.tangent = normalize(output.tangent);
// Calculate the binormal vector against the world matrix only and then normalize the final value. output.binormal = mul(input.binormal, (float3x3)worldMatrix); output.binormal = normalize(output.binormal);
return output; }
Normalmap.ps
//////////////////////////////////////////////////////////////////////////////// // Filename: normalmap.ps ////////////////////////////////////////////////////////////////////////////////
///////////// // GLOBALS // ///////////// The normal map shader requires two textures. The first texture grants access to the color texture. The second texture grants access to the normal map. And just like most or our light shaders the direction and color of the light is provided in the constant buffer as input for lighting calculations.
Texture2D shaderTexture1 : register(t0); Texture2D shaderTexture2 : register(t1); SamplerState SampleType : register(s0);
cbuffer LightBuffer { float4 diffuseColor; float3 lightDirection; float padding; };
////////////// // TYPEDEFS // ////////////// The PixelInputType is also updated in the pixel shader to have the tangent and binormal vectors.
struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL; }; The pixel shader works as we described above with a couple additional lines of code. First, we sample the pixel from the color texture and the normal map. We then multiply the normal map value by two and then subtract one to move it into the -1.0 to +1.0 float range. We have to do this because the sampled value that is presented to us in the 0.0 to +1.0 texture range which only covers half the range we need for bump map normal calculations. After that we then calculate the bump normal which uses the equation we described earlier. This bump normal is normalized and then used to determine the light intensity at this pixel by doing a dot product with the light direction. Once we have the light intensity at this pixel the bump mapping is now done. We use the light intensity with the light color and texture color to get the final pixel color.
//////////////////////////////////////////////////////////////////////////////// // Pixel Shader //////////////////////////////////////////////////////////////////////////////// float4 NormalMapPixelShader(PixelInputType input) : SV_TARGET { float4 textureColor; float4 bumpMap; float3 bumpNormal; float3 lightDir; float lightIntensity; float4 color;
// Sample the pixel color from the color texture at this location. textureColor = shaderTexture1.Sample(SampleType, input.tex);
// Sample the pixel from the normal map. bumpMap = shaderTexture2.Sample(SampleType, input.tex);
// Expand the range of the normal value from (0, +1) to (-1, +1). bumpMap = (bumpMap * 2.0f) - 1.0f;
// Calculate the normal from the data in the normal map. bumpNormal = (bumpMap.x * input.tangent) + (bumpMap.y * input.binormal) + (bumpMap.z * input.normal);
// Normalize the resulting bump normal. bumpNormal = normalize(bumpNormal);
// Invert the light direction for calculations. lightDir = -lightDirection;
// Calculate the amount of light on this pixel based on the normal map value. lightIntensity = saturate(dot(bumpNormal, lightDir));
// Determine the final amount of diffuse color based on the diffuse color combined with the light intensity. color = saturate(diffuseColor * lightIntensity);
// Combine the final light color with the texture color. color = color * textureColor;
return color; }
Normalmapshaderclass.h
The NormalMapShader is similar to the lighting shaders in the previous tutorials with the addition of extra variables to handle normal mapping.
//////////////////////////////////////////////////////////////////////////////// // Filename: normalmapshaderclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _NORMALMAPSHADERCLASS_H_ #define _NORMALMAPSHADERCLASS_H_
////////////// // INCLUDES // ////////////// #include #include #include #include using namespace DirectX; using namespace std;
//////////////////////////////////////////////////////////////////////////////// // Class name: NormalMapShaderClass //////////////////////////////////////////////////////////////////////////////// class NormalMapShaderClass { private: struct MatrixBufferType { XMMATRIX world; XMMATRIX view; XMMATRIX projection; }; We will need a LightBufferType for the light direction and light color.
struct LightBufferType { XMFLOAT4 diffuseColor; XMFLOAT3 lightDirection; float padding; };
public: NormalMapShaderClass(); NormalMapShaderClass(const NormalMapShaderClass&); ~NormalMapShaderClass();
bool Initialize(ID3D11Device*, HWND); void Shutdown(); bool Render(ID3D11DeviceContext*, int, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*, ID3D11ShaderResourceView*, XMFLOAT3, XMFLOAT4);
private: bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*); void ShutdownShader(); void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
bool SetShaderParameters(ID3D11DeviceContext*, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*, ID3D11ShaderResourceView*, XMFLOAT3, XMFLOAT4); void RenderShader(ID3D11DeviceContext*, int);
private: ID3D11VertexShader* m_vertexShader; ID3D11PixelShader* m_pixelShader; ID3D11InputLayout* m_layout; ID3D11Buffer* m_matrixBuffer; ID3D11SamplerState* m_sampleState; The normal map shader will require a constant buffer to interface with the light direction and light color.
ID3D11Buffer* m_lightBuffer; };
#endif
Normalmapshaderclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: normalmapshaderclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "normalmapshaderclass.h"
NormalMapShaderClass::NormalMapShaderClass() { m_vertexShader = 0; m_pixelShader = 0; m_layout = 0; m_matrixBuffer = 0; m_sampleState = 0; m_lightBuffer = 0; }
NormalMapShaderClass::NormalMapShaderClass(const NormalMapShaderClass& other) { }
NormalMapShaderClass::~NormalMapShaderClass() { }
bool NormalMapShaderClass::Initialize(ID3D11Device* device, HWND hwnd) { bool result; wchar_t vsFilename[128]; wchar_t psFilename[128]; int error; The Initialize function will call the shader to load the normal map HLSL files.
// Set the filename of the vertex shader. error = wcscpy_s(vsFilename, 128, L"../Engine/normalmap.vs"); if(error != 0) { return false; }
// Set the filename of the pixel shader. error = wcscpy_s(psFilename, 128, L"../Engine/normalmap.ps"); if(error != 0) { return false; }
// Initialize the vertex and pixel shaders. result = InitializeShader(device, hwnd, vsFilename, psFilename); if(!result) { return false; }
return true; }
void NormalMapShaderClass::Shutdown() { // Shutdown the vertex and pixel shaders as well as the related objects. ShutdownShader();
return; } The Render function will require the matrices, the color and normal texture, and the light direction and light color as inputs.
bool NormalMapShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix, ID3D11ShaderResourceView* texture1, ID3D11ShaderResourceView* texture2, XMFLOAT3 lightDirection, XMFLOAT4 diffuseColor) { bool result;
// Set the shader parameters that it will use for rendering. result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture1, texture2, lightDirection, diffuseColor); if(!result) { return false; }
// Now render the prepared buffers with the shader. RenderShader(deviceContext, indexCount);
return true; }
bool NormalMapShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename) { HRESULT result; ID3D10Blob* errorMessage; ID3D10Blob* vertexShaderBuffer; ID3D10Blob* pixelShaderBuffer; The polygon layout is now set to five elements to accommodate the tangent and binormal.
D3D11_INPUT_ELEMENT_DESC polygonLayout[5]; unsigned int numElements; D3D11_BUFFER_DESC matrixBufferDesc; D3D11_SAMPLER_DESC samplerDesc; D3D11_BUFFER_DESC lightBufferDesc;
// Initialize the pointers this function will use to null. errorMessage = 0; vertexShaderBuffer = 0; pixelShaderBuffer = 0; The normal map vertex shader is loaded here.
// Compile the vertex shader code. result = D3DCompileFromFile(vsFilename, NULL, NULL, "NormalMapVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &vertexShaderBuffer, &errorMessage); if(FAILED(result)) { // If the shader failed to compile it should have writen something to the error message. if(errorMessage) { OutputShaderErrorMessage(errorMessage, hwnd, vsFilename); } // If there was nothing in the error message then it simply could not find the shader file itself. else { MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK); }
return false; } The normal map pixel shader is loaded here.
// Compile the pixel shader code. result = D3DCompileFromFile(psFilename, NULL, NULL, "NormalMapPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &pixelShaderBuffer, &errorMessage); if(FAILED(result)) { // If the shader failed to compile it should have writen something to the error message. if(errorMessage) { OutputShaderErrorMessage(errorMessage, hwnd, psFilename); } // If there was nothing in the error message then it simply could not find the file itself. else { MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK); }
return false; }
// Create the vertex shader from the buffer. result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &m_vertexShader); if(FAILED(result)) { return false; }
// Create the pixel shader from the buffer. result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader); if(FAILED(result)) { return false; }
// Create the vertex input layout de ion. polygonLayout[0].SemanticName = "POSITION"; polygonLayout[0].SemanticIndex = 0; polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT; polygonLayout[0].InputSlot = 0; polygonLayout[0].AlignedByteOffset = 0; polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[0].InstanceDataStepRate = 0;
polygonLayout[1].SemanticName = "TEXCOORD"; polygonLayout[1].SemanticIndex = 0; polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT; polygonLayout[1].InputSlot = 0; polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[1].InstanceDataStepRate = 0;
polygonLayout[2].SemanticName = "NORMAL"; polygonLayout[2].SemanticIndex = 0; polygonLayout[2].Format = DXGI_FORMAT_R32G32B32_FLOAT; polygonLayout[2].InputSlot = 0; polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[2].InstanceDataStepRate = 0; The layout now includes a tangent and binormal element which are setup the same as the normal element with the exception of the semantic name.
polygonLayout[3].SemanticName = "TANGENT"; polygonLayout[3].SemanticIndex = 0; polygonLayout[3].Format = DXGI_FORMAT_R32G32B32_FLOAT; polygonLayout[3].InputSlot = 0; polygonLayout[3].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; polygonLayout[3].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[3].InstanceDataStepRate = 0;
polygonLayout[4].SemanticName = "BINORMAL"; polygonLayout[4].SemanticIndex = 0; polygonLayout[4].Format = DXGI_FORMAT_R32G32B32_FLOAT; polygonLayout[4].InputSlot = 0; polygonLayout[4].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; polygonLayout[4].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[4].InstanceDataStepRate = 0;
// Get a count of the elements in the layout. numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);
// Create the vertex input layout. result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &m_layout); if(FAILED(result)) { return false; }
// Release the vertex shader buffer and pixel shader buffer since they are no longer needed. vertexShaderBuffer->Release(); vertexShaderBuffer = 0;
pixelShaderBuffer->Release(); pixelShaderBuffer = 0;
// Setup the de ion of the dynamic matrix constant buffer that is in the vertex shader. matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC; matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType); matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; matrixBufferDesc.MiscFlags = 0; matrixBufferDesc.StructureByteStride = 0;
// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer); if(FAILED(result)) { return false; }
// Create a texture sampler state de ion. samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.MipLODBias = 0.0f; samplerDesc.MaxAnisotropy = 1; samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; samplerDesc.BorderColor[0] = 0; samplerDesc.BorderColor[1] = 0; samplerDesc.BorderColor[2] = 0; samplerDesc.BorderColor[3] = 0; samplerDesc.MinLOD = 0; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
// Create the texture sampler state. result = device->CreateSamplerState(&samplerDesc, &m_sampleState); if(FAILED(result)) { return false; } The light constant buffer is setup here.
// Setup the de ion of the light dynamic constant buffer that is in the pixel shader. lightBufferDesc.Usage = D3D11_USAGE_DYNAMIC; lightBufferDesc.ByteWidth = sizeof(LightBufferType); lightBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; lightBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; lightBufferDesc.MiscFlags = 0; lightBufferDesc.StructureByteStride = 0;
// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. result = device->CreateBuffer(&lightBufferDesc, NULL, &m_lightBuffer); if(FAILED(result)) { return false; }
return true; }
void NormalMapShaderClass::ShutdownShader() { // Release the light constant buffer. if(m_lightBuffer) { m_lightBuffer->Release(); m_lightBuffer = 0; }
// Release the sampler state. if(m_sampleState) { m_sampleState->Release(); m_sampleState = 0; }
// Release the matrix constant buffer. if(m_matrixBuffer) { m_matrixBuffer->Release(); m_matrixBuffer = 0; }
// Release the layout. if(m_layout) { m_layout->Release(); m_layout = 0; }
// Release the pixel shader. if(m_pixelShader) { m_pixelShader->Release(); m_pixelShader = 0; }
// Release the vertex shader. if(m_vertexShader) { m_vertexShader->Release(); m_vertexShader = 0; }
return; }
void NormalMapShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename) { char* compileErrors; unsigned long long bufferSize, i; ofstream fout;
// Get a pointer to the error message text buffer. compileErrors = (char*)(errorMessage->GetBufferPointer());
// Get the length of the message. bufferSize = errorMessage->GetBufferSize();
// Open a file to write the error message to. fout.open("shader-error.txt");
// Write out the error message. for(i=0; iMap(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if(FAILED(result)) { return false; }
// Get a pointer to the data in the constant buffer. dataPtr = (MatrixBufferType*)mappedResource.pData;
// Copy the matrices into the constant buffer. dataPtr->world = worldMatrix; dataPtr->view = viewMatrix; dataPtr->projection = projectionMatrix;
// Unlock the constant buffer. deviceContext->Unmap(m_matrixBuffer, 0);
// Set the position of the constant buffer in the vertex shader. bufferNumber = 0;
// Finally set the constant buffer in the vertex shader with the updated values. deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer); Here the color texture and normal texture are set in pixel shader.
// Set shader texture resources in the pixel shader. deviceContext->PSSetShaderResources(0, 1, &texture1); deviceContext->PSSetShaderResources(1, 1, &texture2); The light buffer in the pixel shader is then set with the diffuse light color and light direction.
// Lock the light constant buffer so it can be written to. result = deviceContext->Map(m_lightBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if(FAILED(result)) { return false; }
// Get a pointer to the data in the constant buffer. dataPtr2 = (LightBufferType*)mappedResource.pData;
// Copy the lighting variables into the constant buffer. dataPtr2->diffuseColor = diffuseColor; dataPtr2->lightDirection = lightDirection; dataPtr2->padding = 0.0f;
// Unlock the constant buffer. deviceContext->Unmap(m_lightBuffer, 0);
// Set the position of the light constant buffer in the pixel shader. bufferNumber = 0;
// Finally set the light constant buffer in the pixel shader with the updated values. deviceContext->PSSetConstantBuffers(bufferNumber, 1, &m_lightBuffer);
return true; }
void NormalMapShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount) { // Set the vertex input layout. deviceContext->IASetInputLayout(m_layout);
// Set the vertex and pixel shaders that will be used to render this triangle. deviceContext->VSSetShader(m_vertexShader, NULL, 0); deviceContext->PSSetShader(m_pixelShader, NULL, 0);
// Set the sampler state in the pixel shader. deviceContext->PSSetSamplers(0, 1, &m_sampleState);
// Render the triangle. deviceContext->DrawIndexed(indexCount, 0, 0);
return; }
Modelclass.h
//////////////////////////////////////////////////////////////////////////////// // Filename: modelclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _MODELCLASS_H_ #define _MODELCLASS_H_
////////////// // INCLUDES // ////////////// #include #include #include using namespace DirectX; using namespace std;
/////////////////////// // MY CLASS INCLUDES // /////////////////////// #include "textureclass.h"
//////////////////////////////////////////////////////////////////////////////// // Class name: ModelClass //////////////////////////////////////////////////////////////////////////////// class ModelClass { private: The VertexType structure has been changed to now have a tangent and binormal vector. The ModelType structure has also been changed to have a corresponding tangent and binormal vector.
struct VertexType { XMFLOAT3 position; XMFLOAT2 texture; XMFLOAT3 normal; XMFLOAT3 tangent; XMFLOAT3 binormal; };
struct ModelType { float x, y, z; float tu, tv; float nx, ny, nz; float tx, ty, tz; float bx, by, bz; }; The following two structures will be used for calculating the tangent and binormal.
struct TempVertexType { float x, y, z; float tu, tv; float nx, ny, nz; };
struct VectorType { float x, y, z; };
public: ModelClass(); ModelClass(const ModelClass&); ~ModelClass(); Note that Initialize and LoadTextures only take two texture file names for this tutorial.
bool Initialize(ID3D11Device*, ID3D11DeviceContext*, char*, char*, char*); void Shutdown(); void Render(ID3D11DeviceContext*);
int GetIndexCount(); ID3D11ShaderResourceView* GetTexture(int);
private: bool InitializeBuffers(ID3D11Device*); void ShutdownBuffers(); void RenderBuffers(ID3D11DeviceContext*);
bool LoadTextures(ID3D11Device*, ID3D11DeviceContext*, char*, char*); void ReleaseTextures();
bool LoadModel(char*); void ReleaseModel(); We have two new functions for calculating the tangent and binormal vectors for the model.
void CalculateModelVectors(); void CalculateTangentBinormal(TempVertexType, TempVertexType, TempVertexType, VectorType&, VectorType&);
private: ID3D11Buffer *m_vertexBuffer, *m_indexBuffer; int m_vertexCount, m_indexCount; TextureClass* m_Textures; ModelType* m_model; };
#endif
Modelclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: modelclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "modelclass.h"
ModelClass::ModelClass() { m_vertexBuffer = 0; m_indexBuffer = 0; m_Textures = 0; m_model = 0; }
ModelClass::ModelClass(const ModelClass& other) { }
ModelClass::~ModelClass() { }
bool ModelClass::Initialize(ID3D11Device* device, ID3D11DeviceContext* deviceContext, char* modelFilename, char* textureFilename1, char* textureFilename2) { bool result;
// Load in the model data. result = LoadModel(modelFilename); if(!result) { return false; } After the model data has been loaded, we now call the new CalculateModelVectors function to calculate the tangent and binormal and then store it in the model structure.
// Calculate the tangent and binormal vectors for the model. CalculateModelVectors();
// Initialize the vertex and index buffers. result = InitializeBuffers(device); if(!result) { return false; } The model for this tutorial loads just two textures. One is the color texture, and the other is the normal map.
// Load the textures for this model. result = LoadTextures(device, deviceContext, textureFilename1, textureFilename2); if(!result) { return false; }
return true; }
void ModelClass::Shutdown() { // Release the model textures. ReleaseTextures();
// Shutdown the vertex and index buffers. ShutdownBuffers();
// Release the model data. ReleaseModel();
return; }
void ModelClass::Render(ID3D11DeviceContext* deviceContext) { // Put the vertex and index buffers on the graphics pipeline to prepare them for drawing. RenderBuffers(deviceContext);
return; }
int ModelClass::GetIndexCount() { return m_indexCount; }
ID3D11ShaderResourceView* ModelClass::GetTexture(int index) { return m_Textures[index].GetTexture(); }
bool ModelClass::InitializeBuffers(ID3D11Device* device) { VertexType* vertices; unsigned long* indices; D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc; D3D11_SUBRESOURCE_DATA vertexData, indexData; HRESULT result; int i;
// Create the vertex array. vertices = new VertexType[m_vertexCount];
// Create the index array. indices = new unsigned long[m_indexCount]; The InitializeBuffers function has changed at this point where the vertex array is loaded with data from the ModelType array. The ModelType array now has tangent and binormal values for the model so they need to be copied into the vertex array which will then be copied into the vertex buffer.
// Load the vertex array and index array with data. for(i=0; iCreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer); if(FAILED(result)) { return false; }
// Set up the de ion of the static index buffer. indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; indexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the index data. indexData.pSysMem = indices; indexData.SysMemPitch = 0; indexData.SysMemSlicePitch = 0;
// Create the index buffer. result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer); if(FAILED(result)) { return false; }
// Release the arrays now that the vertex and index buffers have been created and loaded. delete [] vertices; vertices = 0;
delete [] indices; indices = 0;
return true; }
void ModelClass::ShutdownBuffers() { // Release the index buffer. if(m_indexBuffer) { m_indexBuffer->Release(); m_indexBuffer = 0; }
// Release the vertex buffer. if(m_vertexBuffer) { m_vertexBuffer->Release(); m_vertexBuffer = 0; }
return; }
void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext) { unsigned int stride; unsigned int offset;
// Set vertex buffer stride and offset. stride = sizeof(VertexType); offset = 0; // Set the vertex buffer to active in the input assembler so it can be rendered. deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
// Set the index buffer to active in the input assembler so it can be rendered. deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles. deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
return; } The LoadTextures and ReleaseTextures will now load and release our color texture and our normal map texture.
bool ModelClass::LoadTextures(ID3D11Device* device, ID3D11DeviceContext* deviceContext, char* filename1, char* filename2) { bool result;
// Create and initialize the texture object array. m_Textures = new TextureClass[2];
result = m_Textures[0].Initialize(device, deviceContext, filename1); if(!result) { return false; }
result = m_Textures[1].Initialize(device, deviceContext, filename2); if(!result) { return false; }
return true; }
void ModelClass::ReleaseTextures() { // Release the texture object array. if(m_Textures) { m_Textures[0].Shutdown(); m_Textures[1].Shutdown();
delete [] m_Textures; m_Textures = 0; }
return; } Although our model format has changed to add a tangent and binormal, we will load models with the same original format as the other tutorials. All we do in this tutorial is calculate the tangent and binormal after the model has been loaded, and then add it to the model manually. So, the LoadModel function has stayed the same for this tutorial.
bool ModelClass::LoadModel(char* filename) { ifstream fin; char input; int i;
// Open the model file. fin.open(filename);
// If it could not open the file then exit. if(fin.fail()) { return false; }
// Read up to the value of vertex count. fin.get(input); while (input != ':') { fin.get(input); }
// Read in the vertex count. fin >> m_vertexCount;
// Set the number of indices to be the same as the vertex count. m_indexCount = m_vertexCount;
// Create the model using the vertex count that was read in. m_model = new ModelType[m_vertexCount];
// Read up to the beginning of the data. fin.get(input); while (input != ':') { fin.get(input); } fin.get(input); fin.get(input);
// Read in the vertex data. for(i=0; i> m_model[i].x >> m_model[i].y >> m_model[i].z; fin >> m_model[i].tu >> m_model[i].tv; fin >> m_model[i].nx >> m_model[i].ny >> m_model[i].nz; }
// Close the model file. fin.close();
return true; }
void ModelClass::ReleaseModel() { if(m_model) { delete [] m_model; m_model = 0; }
return; } CalculateModelVectors generates the tangent and binormal for the model. To start it calculates how many faces (triangles) are in the model. Then for each of those triangles it gets the three vertices and uses that to calculate the tangent and binormal. After calculating those two new normal vectors, it then saves them back into the model structure.
void ModelClass::CalculateModelVectors() { int faceCount, i, index; TempVertexType vertex1, vertex2, vertex3; VectorType tangent, binormal;
// Calculate the number of faces in the model. faceCount = m_vertexCount / 3;
// Initialize the index to the model data. index = 0;
// Go through all the faces and calculate the the tangent and binormal vectors. for(i=0; iInitialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR); if(!result) { MessageBox(hwnd, L"Could not initialize Direct3D", L"Error", MB_OK); return false; }
// Create and initialize the camera object. m_Camera = new CameraClass;
m_Camera->SetPosition(0.0f, 0.0f, -5.0f); m_Camera->Render(); The new NormalMapShaderClass is created here.
// Create and initialize the normal map shader object. m_NormalMapShader = new NormalMapShaderClass;
result = m_NormalMapShader->Initialize(m_Direct3D->GetDevice(), hwnd); if(!result) { MessageBox(hwnd, L"Could not initialize the normal map shader object.", L"Error", MB_OK); return false; } We will load the cube model.
// Set the file name of the model. strcpy_s(modelFilename, "../Engine/data/cube.txt"); Here we load the stone color texture and the new normal map texture.
// Set the file name of the textures. strcpy_s(textureFilename1, "../Engine/data/stone01.tga"); strcpy_s(textureFilename2, "../Engine/data/normal01.tga");
// Create and initialize the model object. m_Model = new ModelClass;
result = m_Model->Initialize(m_Direct3D->GetDevice(), m_Direct3D->GetDeviceContext(), modelFilename, textureFilename1, textureFilename2); if(!result) { return false; } We also create a basic directional white light for the normal mapping to work.
// Create and initialize the light object. m_Light = new LightClass;
m_Light->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f); m_Light->SetDirection(0.0f, 0.0f, 1.0f);
return true; } The Shutdown function will release our new NormalMapShaderClass and LightClass objects.
void ApplicationClass::Shutdown() { // Release the light object. if(m_Light) { delete m_Light; m_Light = 0; }
// Release the model object. if(m_Model) { m_Model->Shutdown(); delete m_Model; m_Model = 0; }
// Release the normal map shader object. if(m_NormalMapShader) { m_NormalMapShader->Shutdown(); delete m_NormalMapShader; m_NormalMapShader = 0; }
// Release the camera object. if(m_Camera) { delete m_Camera; m_Camera = 0; }
// Release the Direct3D object. if(m_Direct3D) { m_Direct3D->Shutdown(); delete m_Direct3D; m_Direct3D = 0; }
return; }
bool ApplicationClass::Frame(InputClass* Input) { static float rotation = 360.0f; bool result;
// Check if the user pressed escape and wants to exit the application. if(Input->IsEscapePressed()) { return false; }
// Update the rotation variable each frame. rotation -= 0.0174532925f * 0.25f; if(rotation BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
// Get the world, view, and projection matrices from the camera and d3d objects. m_Direct3D->GetWorldMatrix(worldMatrix); m_Camera->GetViewMatrix(viewMatrix); m_Direct3D->GetProjectionMatrix(projectionMatrix); Rotate the cube model each frame to show the effect.
// Rotate the world matrix by the rotation value so that the model will spin. worldMatrix = XMMatrixRotationY(rotation);
// Render the model using the normal map shader. m_Model->Render(m_Direct3D->GetDeviceContext()); Render the model using the normal map shader.
result = m_NormalMapShader->Render(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture(0), m_Model->GetTexture(1), m_Light->GetDirection(), m_Light->GetDiffuseColor()); if(!result) { return false; }
// Present the rendered scene to the screen. m_Direct3D->EndScene();
return true; }
Summary
With the normal map shader, you can create very detailed scenes that look 3D with just two 2D textures.
| |
| меню : 1239 |
In this tutorial we will cover how to create and render sprites using DirectX 11. We will also learn the basics of using system timers. This tutorial builds upon the knowledge from the previous tutorial on how to render 2D bitmap images.
A sprite is an animated image made up of multiple static 2D bitmap images that are rendered quickly in a sequence. For example, in this tutorial we will create four simple 2D images and save them in targa format. They look like the following:
Then we will load those four images into a texture array and render them in a sequence to give the illusion of an animated square that has four rotating colors.
Now the way we do that is very simple. We just reuse the BitmapClass from the previous tutorial and give it an array of textures instead of just one. We will also update it every frame with the current frame time so that it can smoothly cycle through the four textures.
We will also create a file to represent the data needed to load and render our sprite. Here is the file we use for this tutorial:
sprite_data_01.txt
4 ../Engine/data/sprite01.tga ../Engine/data/sprite02.tga ../Engine/data/sprite03.tga ../Engine/data/sprite04.tga 250 The format is simple. The first line tells us how many targa images will make up the sprite. The following lines are the filenames of each targa image that make up the sprite. And the last line is the speed in milliseconds that we want the sprite to cycle the images at.
Data Driven Design
Now I will take a quick moment to discuss data driven design. The idea of using text files, tools with sliders and knobs, and so forth to control the contents or flow of the program is called data driven design. You want to be able to quickly make changes, sometimes by many people at once, to the same program. Sometimes even while the program is running. And you definitely want to be able to do this without ever having to recompile.
So, in this example here we have a file that controls the basics of how the sprite works, instead of hardcoding any of this into the program. You can simply modify the text file and run the program again and the changes will take effect.
Framework
The framework has changed by removing the BitmapClass and replacing it with the SpriteClass. We also add a new class named TimerClass which will record the milliseconds between each frame so that classes like SpriteClass can use it for timing things like animation.
Spriteclass.h
The SpriteClass is just the BitmapClass re-written to include an array of textures. It also includes a frame timer assist smoothly cycling through the textures that get mapped to the 2D square.
//////////////////////////////////////////////////////////////////////////////// // Filename: spriteclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _SPRITECLASS_H_ #define _SPRITECLASS_H_
////////////// // INCLUDES // ////////////// #include #include using namespace DirectX; using namespace std;
/////////////////////// // MY CLASS INCLUDES // /////////////////////// #include "textureclass.h"
//////////////////////////////////////////////////////////////////////////////// // Class name: SpriteClass //////////////////////////////////////////////////////////////////////////////// class SpriteClass { private: struct VertexType { XMFLOAT3 position; XMFLOAT2 texture; };
public: SpriteClass(); SpriteClass(const SpriteClass&); ~SpriteClass();
bool Initialize(ID3D11Device*, ID3D11DeviceContext*, int, int, char*, int, int); void Shutdown(); bool Render(ID3D11DeviceContext*); The new Update function will need to be called each Frame with the speed of the frame as input.
void Update(float);
int GetIndexCount(); ID3D11ShaderResourceView* GetTexture();
void SetRenderLocation(int, int);
private: bool InitializeBuffers(ID3D11Device*); void ShutdownBuffers(); bool UpdateBuffers(ID3D11DeviceContext*); void RenderBuffers(ID3D11DeviceContext*);
bool LoadTextures(ID3D11Device*, ID3D11DeviceContext*, char*); void ReleaseTextures();
private: ID3D11Buffer *m_vertexBuffer, *m_indexBuffer; int m_vertexCount, m_indexCount, m_screenWidth, m_screenHeight, m_bitmapWidth, m_bitmapHeight, m_renderX, m_renderY, m_prevPosX, m_prevPosY; TextureClass* m_Textures; float m_frameTime, m_cycleTime; int m_currentTexture, m_textureCount; };
#endif
Spriteclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: spriteclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "spriteclass.h"
SpriteClass::SpriteClass() { m_vertexBuffer = 0; m_indexBuffer = 0; m_Textures = 0; }
SpriteClass::SpriteClass(const SpriteClass& other) { }
SpriteClass::~SpriteClass() { } The Initialize function works the same as the BitmapClass mostly, but now it takes in a spriteFilename which gives access to the text file that contains the definition of the sprite.
bool SpriteClass::Initialize(ID3D11Device* device, ID3D11DeviceContext* deviceContext, int screenWidth, int screenHeight, char* spriteFilename, int renderX, int renderY) { bool result;
// Store the screen size. m_screenWidth = screenWidth; m_screenHeight = screenHeight;
// Store where the sprite should be rendered to. m_renderX = renderX; m_renderY = renderY; We also now initialize the frame time. This will be used to control the sprite cycling speed.
// Initialize the frame time for this sprite object. m_frameTime = 0;
// Initialize the vertex and index buffer that hold the geometry for the sprite bitmap. result = InitializeBuffers(device); if(!result) { return false; }
// Load the textures for this sprite. result = LoadTextures(device, deviceContext, spriteFilename); if(!result) { return false; }
return true; }
void SpriteClass::Shutdown() { // Release the textures used for this sprite. ReleaseTextures();
// Release the vertex and index buffers. ShutdownBuffers();
return; }
bool SpriteClass::Render(ID3D11DeviceContext* deviceContext) { bool result;
// Update the buffers if the position of the sprite has changed from its original position. result = UpdateBuffers(deviceContext); if(!result) { return false; }
// Put the vertex and index buffers on the graphics pipeline to prepare them for drawing. RenderBuffers(deviceContext);
return true; } The Update function takes in the frame time each frame. This will usually be around 16-17ms if you are running your program at 60fps. Each frame we add this time to the m_frameTime counter. If it reaches or passes the cycle time that was defined for this sprite, then we change the sprite to use the next texture in the array. We then reset the timer to start from zero again.
void SpriteClass::Update(float frameTime) { // Increment the frame time each frame. m_frameTime += frameTime;
// Check if the frame time has reached the cycle time. if(m_frameTime >= m_cycleTime) { // If it has then reset the frame time and cycle to the next sprite in the texture array. m_frameTime -= m_cycleTime;
m_currentTexture++;
// If we are at the last sprite texture then go back to the beginning of the texture array to the first texture again. if(m_currentTexture == m_textureCount) { m_currentTexture = 0; } }
return; }
int SpriteClass::GetIndexCount() { return m_indexCount; } The GetTexture function now returns the current texture for the sprite from the texture array.
ID3D11ShaderResourceView* SpriteClass::GetTexture() { return m_Textures[m_currentTexture].GetTexture(); }
bool SpriteClass::InitializeBuffers(ID3D11Device* device) { VertexType* vertices; unsigned long* indices; D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc; D3D11_SUBRESOURCE_DATA vertexData, indexData; HRESULT result; int i;
// Initialize the previous rendering position to negative one. m_prevPosX = -1; m_prevPosY = -1;
// Set the number of vertices in the vertex array. m_vertexCount = 6;
// Set the number of indices in the index array. m_indexCount = m_vertexCount;
// Create the vertex array. vertices = new VertexType[m_vertexCount];
// Create the index array. indices = new unsigned long[m_indexCount];
// Initialize vertex array to zeros at first. memset(vertices, 0, (sizeof(VertexType) * m_vertexCount));
// Load the index array with data. for(i=0; iCreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer); if(FAILED(result)) { return false; }
// Set up the de ion of the index buffer. indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; indexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the index data. indexData.pSysMem = indices; indexData.SysMemPitch = 0; indexData.SysMemSlicePitch = 0;
// Create the index buffer. result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer); if(FAILED(result)) { return false; }
// Release the arrays now that the vertex and index buffers have been created and loaded. delete [] vertices; vertices = 0;
delete [] indices; indices = 0;
return true; }
void SpriteClass::ShutdownBuffers() { // Release the index buffer. if(m_indexBuffer) { m_indexBuffer->Release(); m_indexBuffer = 0; }
// Release the vertex buffer. if(m_vertexBuffer) { m_vertexBuffer->Release(); m_vertexBuffer = 0; }
return; }
bool SpriteClass::UpdateBuffers(ID3D11DeviceContext* deviceContent) { float left, right, top, bottom; VertexType* vertices; D3D11_MAPPED_SUBRESOURCE mappedResource; VertexType* dataPtr; HRESULT result;
// If the position we are rendering this bitmap to hasn't changed then don't update the vertex buffer. if((m_prevPosX == m_renderX) && (m_prevPosY == m_renderY)) { return true; }
// If the rendering location has changed then store the new position and update the vertex buffer. m_prevPosX = m_renderX; m_prevPosY = m_renderY;
// Create the vertex array. vertices = new VertexType[m_vertexCount];
// Calculate the screen coordinates of the left side of the bitmap. left = (float)((m_screenWidth / 2) * -1) + (float)m_renderX;
// Calculate the screen coordinates of the right side of the bitmap. right = left + (float)m_bitmapWidth;
// Calculate the screen coordinates of the top of the bitmap. top = (float)(m_screenHeight / 2) - (float)m_renderY;
// Calculate the screen coordinates of the bottom of the bitmap. bottom = top - (float)m_bitmapHeight;
// Load the vertex array with data. // First triangle. vertices[0].position = XMFLOAT3(left, top, 0.0f); // Top left. vertices[0].texture = XMFLOAT2(0.0f, 0.0f);
vertices[1].position = XMFLOAT3(right, bottom, 0.0f); // Bottom right. vertices[1].texture = XMFLOAT2(1.0f, 1.0f);
vertices[2].position = XMFLOAT3(left, bottom, 0.0f); // Bottom left. vertices[2].texture = XMFLOAT2(0.0f, 1.0f);
// Second triangle. vertices[3].position = XMFLOAT3(left, top, 0.0f); // Top left. vertices[3].texture = XMFLOAT2(0.0f, 0.0f);
vertices[4].position = XMFLOAT3(right, top, 0.0f); // Top right. vertices[4].texture = XMFLOAT2(1.0f, 0.0f);
vertices[5].position = XMFLOAT3(right, bottom, 0.0f); // Bottom right. vertices[5].texture = XMFLOAT2(1.0f, 1.0f);
// Lock the vertex buffer. result = deviceContent->Map(m_vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if(FAILED(result)) { return false; }
// Get a pointer to the data in the constant buffer. dataPtr = (VertexType*)mappedResource.pData;
// Copy the data into the vertex buffer. memcpy(dataPtr, (void*)vertices, (sizeof(VertexType) * m_vertexCount));
// Unlock the vertex buffer. deviceContent->Unmap(m_vertexBuffer, 0);
// Release the pointer reference. dataPtr = 0;
// Release the vertex array as it is no longer needed. delete [] vertices; vertices = 0;
return true; }
void SpriteClass::RenderBuffers(ID3D11DeviceContext* deviceContext) { unsigned int stride; unsigned int offset;
// Set vertex buffer stride and offset. stride = sizeof(VertexType); offset = 0;
// Set the vertex buffer to active in the input assembler so it can be rendered. deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
// Set the index buffer to active in the input assembler so it can be rendered. deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles. deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
return; } The LoadTextures function has been changed from the BitmapClass version. It now opens a file that defines the SpriteClass object. We open the file and read in the number of textures it uses, each tga file used for each of the textures, and the speed at which it should cycle through the textures.
bool SpriteClass::LoadTextures(ID3D11Device* device, ID3D11DeviceContext* deviceContext, char* filename) { char textureFilename[128]; ifstream fin; int i, j; char input; bool result;
// Open the sprite info data file. fin.open(filename); if(fin.fail()) { return false; }
// Read in the number of textures. fin >> m_textureCount;
// Create and initialize the texture array with the texture count from the file. m_Textures = new TextureClass[m_textureCount];
// Read to start of next line. fin.get(input);
// Read in each texture file name. for(i=0; i> m_cycleTime;
// Convert the integer milliseconds to float representation. m_cycleTime = m_cycleTime * 0.001f;
// Close the file. fin.close();
// Get the dimensions of the first texture and use that as the dimensions of the 2D sprite images. m_bitmapWidth = m_Textures[0].GetWidth(); m_bitmapHeight = m_Textures[0].GetHeight();
// Set the starting texture in the cycle to be the first one in the list. m_currentTexture = 0;
return true; } The ReleaseTextures function will release the array of textures that was loaded at the start of the program.
void SpriteClass::ReleaseTextures() { int i;
// Release the texture objects. if(m_Textures) { for(i=0; iInitialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR); if(!result) { MessageBox(hwnd, L"Could not initialize Direct3D", L"Error", MB_OK); return false; }
// Create the camera object. m_Camera = new CameraClass;
// Set the initial position of the camera. m_Camera->SetPosition(0.0f, 0.0f, -10.0f); m_Camera->Render();
// Create and initialize the texture shader object. m_TextureShader = new TextureShaderClass;
result = m_TextureShader->Initialize(m_Direct3D->GetDevice(), hwnd); if(!result) { MessageBox(hwnd, L"Could not initialize the texture shader object.", L"Error", MB_OK); return false; } Here we initialize the new sprite object using the sprite_data_01.txt file.
// Set the sprite info file we will be using. strcpy_s(spriteFilename, "../Engine/data/sprite_data_01.txt");
// Create and initialize the sprite object. m_Sprite = new SpriteClass;
result = m_Sprite->Initialize(m_Direct3D->GetDevice(), m_Direct3D->GetDeviceContext(), screenWidth, screenHeight, spriteFilename, 50, 50); if(!result) { return false; } The new TimerClass object is initialized here.
// Create and initialize the timer object. m_Timer = new TimerClass;
result = m_Timer->Initialize(); if(!result) { return false; }
return true; } In the Shutdown function we will release the new SpriteClass and TimerClass objects.
void ApplicationClass::Shutdown() { // Release the timer object. if(m_Timer) { delete m_Timer; m_Timer = 0; }
// Release the sprite object. if(m_Sprite) { m_Sprite->Shutdown(); delete m_Sprite; m_Sprite = 0; }
// Release the texture shader object. if(m_TextureShader) { m_TextureShader->Shutdown(); delete m_TextureShader; m_TextureShader = 0; }
// Release the camera object. if(m_Camera) { delete m_Camera; m_Camera = 0; }
// Release the Direct3D object. if(m_Direct3D) { m_Direct3D->Shutdown(); delete m_Direct3D; m_Direct3D = 0; }
return; }
bool ApplicationClass::Frame() { float frameTime; bool result; Each frame we update the TimerClass object to record the delta in time since the last frame in milliseconds.
// Update the system stats. m_Timer->Frame();
// Get the current frame time. frameTime = m_Timer->GetTime();
// Update the sprite object using the frame time. m_Sprite->Update(frameTime);
// Render the graphics scene. result = Render(); if(!result) { return false; }
return true; }
bool ApplicationClass::Render() { XMMATRIX worldMatrix, viewMatrix, orthoMatrix; bool result;
// Clear the buffers to begin the scene. m_Direct3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
// Get the world, view, and projection matrices from the camera and d3d objects. m_Direct3D->GetWorldMatrix(worldMatrix); m_Camera->GetViewMatrix(viewMatrix); m_Direct3D->GetOrthoMatrix(orthoMatrix);
// Turn off the Z buffer to begin all 2D rendering. m_Direct3D->TurnZBufferOff(); Now we render the sprite.
// Put the sprite vertex and index buffers on the graphics pipeline to prepare them for drawing. result = m_Sprite->Render(m_Direct3D->GetDeviceContext()); if(!result) { return false; }
// Render the sprite with the texture shader. result = m_TextureShader->Render(m_Direct3D->GetDeviceContext(), m_Sprite->GetIndexCount(), worldMatrix, viewMatrix, orthoMatrix, m_Sprite->GetTexture()); if(!result) { return false; }
// Turn the Z buffer back on now that all 2D rendering has completed. m_Direct3D->TurnZBufferOn();
// Present the rendered scene to the screen. m_Direct3D->EndScene();
return true; }
| |
| меню : 1238 |
This tutorial will explain how to use texturing in DirectX 11. Texturing allows us to add photorealism to our scenes by applying photographs and other images onto polygon faces. For example, in this tutorial we will take the following image:
And then apply it to the polygon from the previous tutorial to produce the following:
The format of the textures we will be using are .tga files. This is the common graphics format that supports red, green, blue, and alpha channels. You can create and edit Targa files with generally any image editing software. And the file format is mostly straight forward.
And before we get into the code, we should discuss how texture mapping works. To map pixels from the .tga image onto the polygon we use what is called the Texel Coordinate System. This system converts the integer value of the pixel into a floating-point value between 0.0f and 1.0f. For example, if a texture width is 256 pixels wide then the first pixel will map to 0.0f, the 256th pixel will map to 1.0f, and a middle pixel of 128 would map to 0.5f.
In the texel coordinate system the width value is named "U" and the height value is named "V". The width goes from 0.0 on the left to 1.0 on the right. The height goes from 0.0 on the top to 1.0 on the bottom. For example, top left would be denoted as U 0.0, V 0.0 and bottom right would be denoted as U 1.0, V 1.0. I have made a diagram below to illustrate this system:
Now that we have a basic understanding of how to map textures onto polygons, we can look at the updated frame work for this tutorial:
The changes to the frame work since the previous tutorial are the new TextureClass which is inside ModelClass and the new TextureShaderClass which replaces the ColorShaderClass. We'll start the code section by looking at the new HLSL texture shader first.
Texture.vs
The texture vertex shader is similar to the previous color shader except that there have been some changes to accommodate texturing.
//////////////////////////////////////////////////////////////////////////////// // Filename: texture.vs ////////////////////////////////////////////////////////////////////////////////
///////////// // GLOBALS // ///////////// cbuffer MatrixBuffer { matrix worldMatrix; matrix viewMatrix; matrix projectionMatrix; }; We are no longer using color in our vertex type and have instead moved to using texture coordinates. Since texture coordinates take a U and V float coordinate, we use float2 as its type. The semantic for texture coordinates is TEXCOORD0 for vertex shaders and pixel shaders. You can change the zero to any number to indicate which set of coordinates you are working with as multiple texture coordinates are allowed.
////////////// // TYPEDEFS // ////////////// struct VertexInputType { float4 position : POSITION; float2 tex : TEXCOORD0; };
struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; };
//////////////////////////////////////////////////////////////////////////////// // Vertex Shader //////////////////////////////////////////////////////////////////////////////// PixelInputType TextureVertexShader(VertexInputType input) { PixelInputType output;
// Change the position vector to be 4 units for proper matrix calculations. input.position.w = 1.0f;
// Calculate the position of the vertex against the world, view, and projection matrices. output.position = mul(input.position, worldMatrix); output.position = mul(output.position, viewMatrix); output.position = mul(output.position, projectionMatrix); The only difference in the texture vertex shader in comparison to the color vertex shader from the previous tutorial is that instead of taking a copy of the color from the input vertex we take a copy of the texture coordinates and pass them to the pixel shader.
// Store the texture coordinates for the pixel shader. output.tex = input.tex;
return output; }
Texture.ps
//////////////////////////////////////////////////////////////////////////////// // Filename: texture.ps //////////////////////////////////////////////////////////////////////////////// The texture pixel shader has two global variables. The first is Texture2D shaderTexture which is the texture resource. This will be our texture resource that will be used for rendering the texture on the model. The second new variable is the SamplerState SampleType. The sampler state allows us to modify how the pixels are written to the polygon face when shaded. For example, if the polygon is really far away and only makes up 8 pixels on the screen then we use the sample state to figure out which pixels or what combination of pixels will actually be drawn from the original texture. The original texture may be 256 pixels by 256 pixels so deciding which pixels get drawn is really important to ensure that the texture still looks decent on the really small polygon face. We will setup the sampler state in the TextureShaderClass also and then attach it to the resource pointer so this pixel shader can use it to determine which sample of pixels to draw.
///////////// // GLOBALS // ///////////// Texture2D shaderTexture : register(t0); SamplerState SampleType : register(s0); The PixelInputType for the texture pixel shader is also modified using texture coordinates instead of the color values.
////////////// // TYPEDEFS // ////////////// struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; }; The pixel shader has been modified so that it now uses the HLSL sample function. The sample function uses the sampler state we defined above and the texture coordinates for this pixel. It uses these two variables to determine and return the pixel value for this UV location on the polygon face.
//////////////////////////////////////////////////////////////////////////////// // Pixel Shader //////////////////////////////////////////////////////////////////////////////// float4 TexturePixelShader(PixelInputType input) : SV_TARGET { float4 textureColor;
// Sample the pixel color from the texture using the sampler at this texture coordinate location. textureColor = shaderTexture.Sample(SampleType, input.tex);
return textureColor; }
Textureshaderclass.h
The TextureShaderClass is just an updated version of the ColorShaderClass from the previous tutorial. This class will be used to draw the 3D models using vertex and pixel shaders.
//////////////////////////////////////////////////////////////////////////////// // Filename: textureshaderclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _TEXTURESHADERCLASS_H_ #define _TEXTURESHADERCLASS_H_
////////////// // INCLUDES // ////////////// #include #include #include #include using namespace DirectX; using namespace std;
//////////////////////////////////////////////////////////////////////////////// // Class name: TextureShaderClass //////////////////////////////////////////////////////////////////////////////// class TextureShaderClass { private: struct MatrixBufferType { XMMATRIX world; XMMATRIX view; XMMATRIX projection; };
public: TextureShaderClass(); TextureShaderClass(const TextureShaderClass&); ~TextureShaderClass();
bool Initialize(ID3D11Device*, HWND); void Shutdown(); bool Render(ID3D11DeviceContext*, int, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*);
private: bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*); void ShutdownShader(); void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
bool SetShaderParameters(ID3D11DeviceContext*, XMMATRIX, XMMATRIX, XMMATRIX, ID3D11ShaderResourceView*); void RenderShader(ID3D11DeviceContext*, int);
private: ID3D11VertexShader* m_vertexShader; ID3D11PixelShader* m_pixelShader; ID3D11InputLayout* m_layout; ID3D11Buffer* m_matrixBuffer; There is a new private variable for the sampler state pointer. This pointer will be used to interface with the texture shader.
ID3D11SamplerState* m_sampleState; };
#endif
Textureshaderclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: textureshaderclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "textureshaderclass.h"
TextureShaderClass::TextureShaderClass() { m_vertexShader = 0; m_pixelShader = 0; m_layout = 0; m_matrixBuffer = 0; The new sampler variable is set to null in the class constructor.
m_sampleState = 0; }
TextureShaderClass::TextureShaderClass(const TextureShaderClass& other) { }
TextureShaderClass::~TextureShaderClass() { }
bool TextureShaderClass::Initialize(ID3D11Device* device, HWND hwnd) { bool result; wchar_t vsFilename[128]; wchar_t psFilename[128]; int error; The new texture.vs and texture.ps HLSL files are loaded for this shader.
// Set the filename of the vertex shader. error = wcscpy_s(vsFilename, 128, L"../Engine/texture.vs"); if(error != 0) { return false; }
// Set the filename of the pixel shader. error = wcscpy_s(psFilename, 128, L"../Engine/texture.ps"); if(error != 0) { return false; }
// Initialize the vertex and pixel shaders. result = InitializeShader(device, hwnd, vsFilename, psFilename); if(!result) { return false; }
return true; } The Shutdown function calls the release of the shader variables.
void TextureShaderClass::Shutdown() { // Shutdown the vertex and pixel shaders as well as the related objects. ShutdownShader();
return; } The Render function now takes a new parameter called texture which is the pointer to the texture resource. This is then sent into the SetShaderParameters function so that the texture can be set in the shader and then used for rendering.
bool TextureShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix, ID3D11ShaderResourceView* texture) { bool result;
// Set the shader parameters that it will use for rendering. result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture); if(!result) { return false; }
// Now render the prepared buffers with the shader. RenderShader(deviceContext, indexCount);
return true; } InitializeShader sets up the texture shader.
bool TextureShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename) { HRESULT result; ID3D10Blob* errorMessage; ID3D10Blob* vertexShaderBuffer; ID3D10Blob* pixelShaderBuffer; D3D11_INPUT_ELEMENT_DESC polygonLayout[2]; unsigned int numElements; D3D11_BUFFER_DESC matrixBufferDesc; We have a new variable to hold the de ion of the texture sampler that will be setup in this function.
D3D11_SAMPLER_DESC samplerDesc;
// Initialize the pointers this function will use to null. errorMessage = 0; vertexShaderBuffer = 0; pixelShaderBuffer = 0; Load in the new texture vertex and pixel shaders.
// Compile the vertex shader code. result = D3DCompileFromFile(vsFilename, NULL, NULL, "TextureVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &vertexShaderBuffer, &errorMessage); if(FAILED(result)) { // If the shader failed to compile it should have writen something to the error message. if(errorMessage) { OutputShaderErrorMessage(errorMessage, hwnd, vsFilename); } // If there was nothing in the error message then it simply could not find the shader file itself. else { MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK); }
return false; }
// Compile the pixel shader code. result = D3DCompileFromFile(psFilename, NULL, NULL, "TexturePixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &pixelShaderBuffer, &errorMessage); if(FAILED(result)) { // If the shader failed to compile it should have writen something to the error message. if(errorMessage) { OutputShaderErrorMessage(errorMessage, hwnd, psFilename); } // If there was nothing in the error message then it simply could not find the file itself. else { MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK); }
return false; }
// Create the vertex shader from the buffer. result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &m_vertexShader); if(FAILED(result)) { return false; }
// Create the pixel shader from the buffer. result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader); if(FAILED(result)) { return false; } The input layout has changed as we now have a texture element instead of color. The first position element stays unchanged but the SemanticName and Format of the second element have been changed to TEXCOORD and DXGI_FORMAT_R32G32_FLOAT. These two changes will now align this layout with our new VertexType in both the ModelClass definition and the typedefs in the shader files.
// Create the vertex input layout de ion. // This setup needs to match the VertexType stucture in the ModelClass and in the shader. polygonLayout[0].SemanticName = "POSITION"; polygonLayout[0].SemanticIndex = 0; polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT; polygonLayout[0].InputSlot = 0; polygonLayout[0].AlignedByteOffset = 0; polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[0].InstanceDataStepRate = 0;
polygonLayout[1].SemanticName = "TEXCOORD"; polygonLayout[1].SemanticIndex = 0; polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT; polygonLayout[1].InputSlot = 0; polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[1].InstanceDataStepRate = 0;
// Get a count of the elements in the layout. numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);
// Create the vertex input layout. result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &m_layout); if(FAILED(result)) { return false; }
// Release the vertex shader buffer and pixel shader buffer since they are no longer needed. vertexShaderBuffer->Release(); vertexShaderBuffer = 0;
pixelShaderBuffer->Release(); pixelShaderBuffer = 0;
// Setup the de ion of the dynamic matrix constant buffer that is in the vertex shader. matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC; matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType); matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; matrixBufferDesc.MiscFlags = 0; matrixBufferDesc.StructureByteStride = 0;
// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer); if(FAILED(result)) { return false; } The sampler state de ion is setup here and then can be passed to the pixel shader after. The most important element of the texture sampler de ion is Filter. Filter will determine how it decides which pixels will be used or combined to create the final look of the texture on the polygon face. In the example here I use D3D11_FILTER_MIN_MAG_MIP_LINEAR which is more expensive in terms of processing but gives the best visual result. It tells the sampler to use linear interpolation for minification, magnification, and mip-level sampling.
AddressU and AddressV are set to Wrap which ensures that the coordinates stay between 0.0f and 1.0f. Anything outside of that wraps around and is placed between 0.0f and 1.0f. All other settings for the sampler state de ion are defaults.
// Create a texture sampler state de ion. samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.MipLODBias = 0.0f; samplerDesc.MaxAnisotropy = 1; samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; samplerDesc.BorderColor[0] = 0; samplerDesc.BorderColor[1] = 0; samplerDesc.BorderColor[2] = 0; samplerDesc.BorderColor[3] = 0; samplerDesc.MinLOD = 0; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
// Create the texture sampler state. result = device->CreateSamplerState(&samplerDesc, &m_sampleState); if (FAILED(result)) { return false; }
return true; } The ShutdownShader function releases all the variables used in the TextureShaderClass.
void TextureShaderClass::ShutdownShader() { The ShutdownShader function now releases the new sampler state that was created during initialization.
// Release the sampler state. if (m_sampleState) { m_sampleState->Release(); m_sampleState = 0; }
// Release the matrix constant buffer. if(m_matrixBuffer) { m_matrixBuffer->Release(); m_matrixBuffer = 0; }
// Release the layout. if(m_layout) { m_layout->Release(); m_layout = 0; }
// Release the pixel shader. if(m_pixelShader) { m_pixelShader->Release(); m_pixelShader = 0; }
// Release the vertex shader. if(m_vertexShader) { m_vertexShader->Release(); m_vertexShader = 0; }
return; } OutputShaderErrorMessage writes out errors to a text file if the HLSL shader could not be loaded.
void TextureShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename) { char* compileErrors; unsigned long long bufferSize, i; ofstream fout;
// Get a pointer to the error message text buffer. compileErrors = (char*)(errorMessage->GetBufferPointer());
// Get the length of the message. bufferSize = errorMessage->GetBufferSize();
// Open a file to write the error message to. fout.open("shader-error.txt");
// Write out the error message. for(i=0; iMap(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if(FAILED(result)) { return false; }
// Get a pointer to the data in the constant buffer. dataPtr = (MatrixBufferType*)mappedResource.pData;
// Copy the matrices into the constant buffer. dataPtr->world = worldMatrix; dataPtr->view = viewMatrix; dataPtr->projection = projectionMatrix;
// Unlock the constant buffer. deviceContext->Unmap(m_matrixBuffer, 0);
// Set the position of the constant buffer in the vertex shader. bufferNumber = 0;
// Finanly set the constant buffer in the vertex shader with the updated values. deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer); The SetShaderParameters function has been modified from the previous tutorial to include setting the texture in the pixel shader now.
// Set shader texture resource in the pixel shader. deviceContext->PSSetShaderResources(0, 1, &texture);
return true; } RenderShader calls the shader technique to render the polygons.
void TextureShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount) { // Set the vertex input layout. deviceContext->IASetInputLayout(m_layout);
// Set the vertex and pixel shaders that will be used to render this triangle. deviceContext->VSSetShader(m_vertexShader, NULL, 0); deviceContext->PSSetShader(m_pixelShader, NULL, 0); The RenderShader function has been changed to include setting the sample state in the pixel shader before rendering.
// Set the sampler state in the pixel shader. deviceContext->PSSetSamplers(0, 1, &m_sampleState);
// Render the triangle. deviceContext->DrawIndexed(indexCount, 0, 0);
return; }
Textureclass.h
The TextureClass encapsulates the loading, unloading, and accessing of a single texture resource. For each texture needed an object of this class must be instantiated.
//////////////////////////////////////////////////////////////////////////////// // Filename: textureclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _TEXTURECLASS_H_ #define _TEXTURECLASS_H_
////////////// // INCLUDES // ////////////// #include #include
//////////////////////////////////////////////////////////////////////////////// // Class name: TextureClass //////////////////////////////////////////////////////////////////////////////// class TextureClass { private: We define the Targa file header structure here to make reading in the data easier.
struct TargaHeader { unsigned char data1[12]; unsigned short width; unsigned short height; unsigned char bpp; unsigned char data2; };
public: TextureClass(); TextureClass(const TextureClass&); ~TextureClass();
bool Initialize(ID3D11Device*, ID3D11DeviceContext*, char*); void Shutdown();
ID3D11ShaderResourceView* GetTexture();
int GetWidth(); int GetHeight();
private: Here we have our Targa reading function. If you wanted to support more formats you would add reading functions here.
bool LoadTarga32Bit(char*);
private: This class has five member variables. The first one holds the raw Targa data read straight in from the file. The second variable called m_texture will hold the structured texture data that DirectX will use for rendering. And the third variable is the resource view that the shader uses to access the texture data when drawing. The width and height are the dimensions of the texture.
unsigned char* m_targaData; ID3D11Texture2D* m_texture; ID3D11ShaderResourceView* m_textureView; int m_width, m_height; };
#endif
Textureclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: textureclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "textureclass.h" Initialize the three pointers to null in the class constructor.
TextureClass::TextureClass() { m_targaData = 0; m_texture = 0; m_textureView = 0; }
TextureClass::TextureClass(const TextureClass& other) { }
TextureClass::~TextureClass() { } The Initialize functions take as input the Direct3D device and the name of the Targa image file. It will first load the Targa data into an array. Then it will create a texture and load the Targa data into it in the correct format (Targa images are upside by default and need to be reversed). Then once the texture is loaded it will create a resource view of the texture for the shader to use for drawing.
bool TextureClass::Initialize(ID3D11Device* device, ID3D11DeviceContext* deviceContext, char* filename) { bool result; int height, width; D3D11_TEXTURE2D_DESC textureDesc; HRESULT hResult; unsigned int rowPitch; D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; So first we call the TextureClass::LoadTarga32Bit function to load the Targa file into the m_targaData array.
// Load the targa image data into memory. result = LoadTarga32Bit(filename); if(!result) { return false; } Next, we need to setup our de ion of the DirectX texture that we will load the Targa data into. We use the height and width from the Targa image data, and set the format to be a 32-bit RGBA texture. We set the SampleDesc to default. Then we set the Usage to D3D11_USAGE_DEFAULT which is the better performing memory, which we will also explain a bit more about down below. And finally, we set the MipLevels, BindFlags, and MiscFlags to the settings required for Mipmaped textures. Once the de ion is complete, we call CreateTexture2D to create an empty texture for us. The next step will be to copy the Targa data into that empty texture.
// Setup the de ion of the texture. textureDesc.Height = m_height; textureDesc.Width = m_width; textureDesc.MipLevels = 0; textureDesc.ArraySize = 1; textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; textureDesc.SampleDesc.Count = 1; textureDesc.SampleDesc.Quality = 0; textureDesc.Usage = D3D11_USAGE_DEFAULT; textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; textureDesc.CPUAccessFlags = 0; textureDesc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;
// Create the empty texture. hResult = device->CreateTexture2D(&textureDesc, NULL, &m_texture); if(FAILED(hResult)) { return false; }
// Set the row pitch of the targa image data. rowPitch = (m_width * 4) * sizeof(unsigned char); Here we use UpdateSubresource to actually do the copying of the Targa data array into the DirectX texture. If you will remember from the previous tutorial, we used Map and Unmap to copy our matrices in the ModelClass into the matrix constant buffer, and we could have done the same here with our texture data. And in fact, using Map and Unmap is generally a lot quicker than using UpdateSubresource, however both loading methods have specific purposes and you need to choose correctly which one to use for performance reasons. The recommendation is that you use Map and Unmap for data that is going to be reloaded each frame or on a very regular basis. And you should use UpdateSubresource for something that will be loaded once or that gets loaded rarely during loading sequences. The reason being is that UpdateSubresource puts the data into higher speed memory that gets cache retention preference since it knows you aren't going to remove or reload it anytime soon. We let DirectX also know by using D3D11_USAGE_DEFAULT when we are going to load using UpdateSubresource. And Map and Unmap will put the data into memory locations that will not be cached as DirectX is expecting that data to be overwritten shortly. And that is why we use D3D11_USAGE_DYNAMIC to notify DirectX that this type of data is transient.
// Copy the targa image data into the texture. deviceContext->UpdateSubresource(m_texture, 0, NULL, m_targaData, rowPitch, 0); After the texture is loaded, we create a shader resource view which allows us to have a pointer to set the texture in shaders. In the de ion we also set two important Mipmap variables which will give us the full range of Mipmap levels for high quality texture rendering at any distance. Once the shader resource view is created, we call GenerateMips and it creates the Mipmaps for us, however if you want you can load your own Mipmap levels in manually if you are looking for even better quality.
// Setup the shader resource view de ion. srvDesc.Format = textureDesc.Format; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Texture2D.MipLevels = -1;
// Create the shader resource view for the texture. hResult = device->CreateShaderResourceView(m_texture, &srvDesc, &m_textureView); if(FAILED(hResult)) { return false; }
// Generate mipmaps for this texture. deviceContext->GenerateMips(m_textureView);
// Release the targa image data now that the image data has been loaded into the texture. delete [] m_targaData; m_targaData = 0;
return true; } The Shutdown function releases the texture data and the three pointers are set to null.
void TextureClass::Shutdown() { // Release the texture view resource. if(m_textureView) { m_textureView->Release(); m_textureView = 0; }
// Release the texture. if(m_texture) { m_texture->Release(); m_texture = 0; }
// Release the targa data. if(m_targaData) { delete [] m_targaData; m_targaData = 0; }
return; } GetTexture is a helper function to provide easy access to the texture view for any shaders that require it for rendering.
ID3D11ShaderResourceView* TextureClass::GetTexture() { return m_textureView; } This is our Targa image loading function. Once again note that Targa images are stored upside down and need to be flipped before using. So here we will open the file, read it into an array, and then take that array data and load it into the m_targaData array in the correct order. Note we are purposely only dealing with 32-bit Targa files that have alpha channels, this function will reject Targa's that are saved as 24-bit.
bool TextureClass::LoadTarga32Bit(char* filename) { int error, bpp, imageSize, index, i, j, k; FILE* filePtr; unsigned int count; TargaHeader targaFileHeader; unsigned char* targaImage;
// Open the targa file for reading in binary. error = fopen_s(&filePtr, filename, "rb"); if(error != 0) { return false; }
// Read in the file header. count = (unsigned int)fread(&targaFileHeader, sizeof(TargaHeader), 1, filePtr); if(count != 1) { return false; }
// Get the important information from the header. m_height = (int)targaFileHeader.height; m_width = (int)targaFileHeader.width; bpp = (int)targaFileHeader.bpp;
// Check that it is 32 bit and not 24 bit. if(bpp != 32) { return false; }
// Calculate the size of the 32 bit image data. imageSize = m_width * m_height * 4;
// Allocate memory for the targa image data. targaImage = new unsigned char[imageSize];
// Read in the targa image data. count = (unsigned int)fread(targaImage, 1, imageSize, filePtr); if(count != imageSize) { return false; }
// Close the file. error = fclose(filePtr); if(error != 0) { return false; }
// Allocate memory for the targa destination data. m_targaData = new unsigned char[imageSize];
// Initialize the index into the targa destination data array. index = 0;
// Initialize the index into the targa image data. k = (m_width * m_height * 4) - (m_width * 4);
// Now copy the targa image data into the targa destination array in the correct order since the targa format is stored upside down and also is not in RGBA order. for(j=0; jRender();
// Get the world, view, and projection matrices from the camera and d3d objects. m_Direct3D->GetWorldMatrix(worldMatrix); m_Camera->GetViewMatrix(viewMatrix); m_Direct3D->GetProjectionMatrix(projectionMatrix);
// Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing. m_Model->Render(m_Direct3D->GetDeviceContext()); The texture shader is called now instead of the color shader to render the model. Notice it also takes the texture resource pointer from the model so the texture shader has access to the texture from the model object.
// Render the model using the texture shader. result = m_TextureShader->Render(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture()); if (!result) { return false; }
// Present the rendered scene to the screen. m_Direct3D->EndScene();
return true; }
| |
|
меню : 1237 |
This tutorial will be the introduction to writing vertex and pixel shaders in DirectX 11. It will also be the introduction to using vertex and index buffers in DirectX 11. These are the most fundamental concepts that you need to understand and utilize to render 3D graphics.
Vertex Buffers
The first concept to understand is vertex buffers. To illustrate this concept let us take the example of a 3D model of a sphere:
The 3D sphere model is actually composed of hundreds of triangles:
Each of the triangles in the sphere model has three points to it, we call each point a vertex. So, for us to render the sphere model we need to put all the vertices that form the sphere into a special data array that we call a vertex buffer. Once all the points of the sphere model are in the vertex buffer, we can then send the vertex buffer to the GPU so that it can render the model.
Index Buffers
Index buffers are related to vertex buffers. Their purpose is to record the location of each vertex that is in the vertex buffer. The GPU then uses the index buffer to quickly find specific vertices in the vertex buffer. The concept of an index buffer is similar to the concept using an index in a book, it helps find the topic you are looking for at a much higher speed. The DirectX documentation says that using index buffers can also increase the possibility of caching the vertex data in faster locations in video memory. So, it is highly advised to use these for performance reasons as well.
Vertex Shaders
Vertex shaders are small programs that are written mainly for transforming the vertices from the vertex buffer into 3D space. There are other calculations that can be done such as calculating normals for each vertex. The vertex shader program will be called by the GPU for each vertex it needs to process. For example, a 5,000 polygon model will run your vertex shader program 15,000 times each frame just to draw that single model. So, if you lock your graphics program to 60 fps it will call your vertex shader 900,000 times a second to draw just 5,000 triangles. As you can tell writing efficient vertex shaders is important.
Pixel Shaders
Pixel shaders are small programs that are written for doing the coloring of the polygons that we draw. They are run by the GPU for every visible pixel that will be drawn to the screen. Coloring, texturing, lighting, and most other effects you plan to do to your polygon faces are handled by the pixel shader program. Pixel shaders must be efficiently written due to the number of times they will be called by the GPU.
HLSL
HLSL is the language we use in DirectX 11 to code these small vertex and pixel shader programs. The syntax is pretty much identical to the C language with some pre-defined types. HLSL program files are composed of global variables, type defines, vertex shaders, pixel shaders, and geometry shaders. As this is the first HLSL tutorial we will do a very simple HLSL program using DirectX 11 to get started.
Updated Framework
The framework has been updated for this tutorial. Under ApplicationClass we have added three new classes called CameraClass, ModelClass, and ColorShaderClass. CameraClass will take care of our view matrix we talked about previously. It will handle the location of the camera in the world and pass it to shaders when they need to draw and figure out where we are looking at the scene from. The ModelClass will handle the geometry of our 3D models, in this tutorial the 3D model will just be a single triangle for simplicity reasons. And finally, ColorShaderClass will be responsible for rendering the model to the screen invoking our HLSL shader.
We will begin the tutorial code by looking at the HLSL shader program first.
Color.vs
These will be our first shader programs. Shaders are small programs that do the actual rendering of models. These shaders are written in HLSL and stored in source files called color.vs and color.ps. I placed the files with the .cpp and .h files in the engine for now. Note that you will want to create a new filter/folder for them in Visual studio. And make sure to right click on the shaders and select "Properties" and in the popup it should have nothing in the Content section and it should say "Does not participate in build" in the Item Type section or you will get compiling errors complaining about main entry point.
Now the purpose of this shader is just to draw colored triangles as I am keeping things simple as possible in this first HLSL tutorial. Here is the code for the vertex shader:
//////////////////////////////////////////////////////////////////////////////// // Filename: color.vs //////////////////////////////////////////////////////////////////////////////// In shader programs you begin with the global variables. These globals can be modified externally from your C++ code. You can use many types of variables such as int or float and then set them externally for the shader program to use. Generally, you will put most globals in buffer object types called "cbuffer" even if it is just a single global variable. Logically organizing these buffers is important for efficient execution of shaders as well as how the graphics card will store the buffers. In this example I've put three matrices in the same buffer since I will update them each frame at the same time.
///////////// // GLOBALS // ///////////// cbuffer MatrixBuffer { matrix worldMatrix; matrix viewMatrix; matrix projectionMatrix; }; Similar to C we can create our own type definitions. We will use different types such as float4 that are available to HLSL which make programming shaders easier and readable. In this example we are creating types that have x, y, z, w position vectors and red, green, blue, alpha colors. The POSITION, COLOR, and SV_POSITION are semantics that convey to the GPU the use of the variable. I have to create two different structures here since the semantics are different for vertex and pixel shaders even though the structures are the same otherwise. POSITION works for vertex shaders and SV_POSITION works for pixel shaders while COLOR works for both. If you want more than one of the same type then you have to add a number to the end such as COLOR0, COLOR1, and so forth.
////////////// // TYPEDEFS // ////////////// struct VertexInputType { float4 position : POSITION; float4 color : COLOR; };
struct PixelInputType { float4 position : SV_POSITION; float4 color : COLOR; }; The vertex shader is called by the GPU when it is processing data from the vertex buffers that have been sent to it. This vertex shader which I named ColorVertexShader will be called for every single vertex in the vertex buffer. The input to the vertex shader must match the data format in the vertex buffer as well as the type definition in the shader source file which in this case is VertexInputType. The output of the vertex shader will be sent to the pixel shader. In this case the output type is called PixelInputType which is defined above as well.
With that in mind you see that the vertex shader creates an output variable that is of the PixelInputType type. It then takes the position of the input vertex and multiplies it by the world, view, and then projection matrices. This will place the vertex in the correct location for rendering in 3D space according to our view and then onto the 2D screen. After that the output variable takes a copy of the input color and then returns the output which will be used as input to the pixel shader. Also note that I do set the W value of the input position to 1.0 otherwise it is undefined since we only read in a XYZ vector for position.
//////////////////////////////////////////////////////////////////////////////// // Vertex Shader //////////////////////////////////////////////////////////////////////////////// PixelInputType ColorVertexShader(VertexInputType input) { PixelInputType output;
// Change the position vector to be 4 units for proper matrix calculations. input.position.w = 1.0f;
// Calculate the position of the vertex against the world, view, and projection matrices. output.position = mul(input.position, worldMatrix); output.position = mul(output.position, viewMatrix); output.position = mul(output.position, projectionMatrix); // Store the input color for the pixel shader to use. output.color = input.color; return output; }
Color.ps
The pixel shader draws each pixel on the polygons that will be rendered to the screen. In this pixel shader it uses PixelInputType as input and returns a float4 as output which represents the final pixel color. This pixel shader program is very simple as we just tell it to color the pixel the same as the input value of the color. Note that the pixel shader gets its input from the vertex shader output.
//////////////////////////////////////////////////////////////////////////////// // Filename: color.ps ////////////////////////////////////////////////////////////////////////////////
////////////// // TYPEDEFS // ////////////// struct PixelInputType { float4 position : SV_POSITION; float4 color : COLOR; };
//////////////////////////////////////////////////////////////////////////////// // Pixel Shader //////////////////////////////////////////////////////////////////////////////// float4 ColorPixelShader(PixelInputType input) : SV_TARGET { return input.color; }
Colorshaderclass.h
The ColorShaderClass is what we will use to invoke our HLSL shaders for drawing the 3D models that are on the GPU.
//////////////////////////////////////////////////////////////////////////////// // Filename: colorshaderclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _COLORSHADERCLASS_H_ #define _COLORSHADERCLASS_H_
////////////// // INCLUDES // ////////////// #include #include #include #include using namespace DirectX; using namespace std;
//////////////////////////////////////////////////////////////////////////////// // Class name: ColorShaderClass //////////////////////////////////////////////////////////////////////////////// class ColorShaderClass { private: Here is the definition of the cBuffer type that will be used with the vertex shader. This typedef must be exactly the same as the one in the vertex shader as the model data needs to match the typedefs in the shader for proper rendering.
struct MatrixBufferType { XMMATRIX world; XMMATRIX view; XMMATRIX projection; };
public: ColorShaderClass(); ColorShaderClass(const ColorShaderClass&); ~ColorShaderClass(); The functions here handle initializing and shutdown of the shader. The render function sets the shader parameters and then draws the prepared model vertices using the shader.
bool Initialize(ID3D11Device*, HWND); void Shutdown(); bool Render(ID3D11DeviceContext*, int, XMMATRIX, XMMATRIX, XMMATRIX);
private: bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*); void ShutdownShader(); void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*);
bool SetShaderParameters(ID3D11DeviceContext*, XMMATRIX, XMMATRIX, XMMATRIX); void RenderShader(ID3D11DeviceContext*, int);
private: ID3D11VertexShader* m_vertexShader; ID3D11PixelShader* m_pixelShader; ID3D11InputLayout* m_layout; ID3D11Buffer* m_matrixBuffer; };
#endif
Colorshaderclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: colorshaderclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "colorshaderclass.h" As usual the class constructor initializes all the private pointers in the class to null.
ColorShaderClass::ColorShaderClass() { m_vertexShader = 0; m_pixelShader = 0; m_layout = 0; m_matrixBuffer = 0; }
ColorShaderClass::ColorShaderClass(const ColorShaderClass& other) { }
ColorShaderClass::~ColorShaderClass() { } The Initialize function will call the initialization function for the shaders. We pass in the name of the HLSL shader files, in this tutorial they are named color.vs and color.ps.
bool ColorShaderClass::Initialize(ID3D11Device* device, HWND hwnd) { bool result; wchar_t vsFilename[128]; wchar_t psFilename[128]; int error;
// Set the filename of the vertex shader. error = wcscpy_s(vsFilename, 128, L"../Engine/color.vs"); if(error != 0) { return false; }
// Set the filename of the pixel shader. error = wcscpy_s(psFilename, 128, L"../Engine/color.ps"); if(error != 0) { return false; }
// Initialize the vertex and pixel shaders. result = InitializeShader(device, hwnd, vsFilename, psFilename); if(!result) { return false; }
return true; } The Shutdown function will call the shutdown of the shader.
void ColorShaderClass::Shutdown() { // Shutdown the vertex and pixel shaders as well as the related objects. ShutdownShader();
return; } Render will first set the parameters inside the shader using the SetShaderParameters function. Once the parameters are set it then calls RenderShader to draw the green triangle using the HLSL shader.
bool ColorShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, XMMATRIX worldMatrix, XMMATRIX viewMatrix, XMMATRIX projectionMatrix) { bool result;
// Set the shader parameters that it will use for rendering. result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix); if(!result) { return false; }
// Now render the prepared buffers with the shader. RenderShader(deviceContext, indexCount);
return true; } Now we will start with one of the more important functions to this tutorial which is called InitializeShader. This function is what actually loads the shader files and makes it usable to DirectX and the GPU. You will also see the setup of the layout and how the vertex buffer data is going to look on the graphics pipeline in the GPU. The layout will need the match the VertexType in the modelclass.h file as well as the one defined in the color.vs file.
bool ColorShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename) { HRESULT result; ID3D10Blob* errorMessage; ID3D10Blob* vertexShaderBuffer; ID3D10Blob* pixelShaderBuffer; D3D11_INPUT_ELEMENT_DESC polygonLayout[2]; unsigned int numElements; D3D11_BUFFER_DESC matrixBufferDesc;
// Initialize the pointers this function will use to null. errorMessage = 0; vertexShaderBuffer = 0; pixelShaderBuffer = 0; Here is where we compile the shader programs into buffers. We give it the name of the shader file, the name of the shader, the shader version (5.0 in DirectX 11), and the buffer to compile the shader into. If it fails compiling the shader it will put an error message inside the errorMessage string which we send to another function to write out the error. If it still fails and there is no errorMessage string then it means it could not find the shader file in which case we pop up a dialog box saying so.
// Compile the vertex shader code. result = D3DCompileFromFile(vsFilename, NULL, NULL, "ColorVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &vertexShaderBuffer, &errorMessage); if(FAILED(result)) { // If the shader failed to compile it should have writen something to the error message. if(errorMessage) { OutputShaderErrorMessage(errorMessage, hwnd, vsFilename); } // If there was nothing in the error message then it simply could not find the shader file itself. else { MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK); }
return false; }
// Compile the pixel shader code. result = D3DCompileFromFile(psFilename, NULL, NULL, "ColorPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, &pixelShaderBuffer, &errorMessage); if(FAILED(result)) { // If the shader failed to compile it should have writen something to the error message. if(errorMessage) { OutputShaderErrorMessage(errorMessage, hwnd, psFilename); } // If there was nothing in the error message then it simply could not find the file itself. else { MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK); }
return false; } Once the vertex shader and pixel shader code has successfully compiled into buffers, we then use those buffers to create the shader objects themselves. We will use these pointers to interface with the vertex and pixel shader from this point forward.
// Create the vertex shader from the buffer. result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &m_vertexShader); if(FAILED(result)) { return false; }
// Create the pixel shader from the buffer. result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader); if(FAILED(result)) { return false; } The next step is to create the layout of the vertex data that will be processed by the shader. As this shader uses a position and color vector, we need to create both in the layout specifying the size of both. The semantic name is the first thing to fill out in the layout, this allows the shader to determine the usage of this element of the layout. As we have two different elements, we use POSITION for the first one and COLOR for the second. The next important part of the layout is the Format. For the position vector we use DXGI_FORMAT_R32G32B32_FLOAT and for the color we use DXGI_FORMAT_R32G32B32A32_FLOAT. The final thing you need to pay attention to is the AlignedByteOffset which indicates how the data is spaced in the buffer. For this layout we are telling it the first 12 bytes are position and the next 16 bytes will be color, AlignedByteOffset shows where each element begins. You can use D3D11_APPEND_ALIGNED_ELEMENT instead of placing your own values in AlignedByteOffset and it will figure out the spacing for you. The other settings I've made default for now as they are not needed in this tutorial.
// Create the vertex input layout de ion. // This setup needs to match the VertexType stucture in the ModelClass and in the shader. polygonLayout[0].SemanticName = "POSITION"; polygonLayout[0].SemanticIndex = 0; polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT; polygonLayout[0].InputSlot = 0; polygonLayout[0].AlignedByteOffset = 0; polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[0].InstanceDataStepRate = 0;
polygonLayout[1].SemanticName = "COLOR"; polygonLayout[1].SemanticIndex = 0; polygonLayout[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; polygonLayout[1].InputSlot = 0; polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[1].InstanceDataStepRate = 0; Once the layout de ion has been setup, we can get the size of it and then create the input layout using the D3D device. Also release the vertex and pixel shader buffers since they are no longer needed once the layout has been created.
// Get a count of the elements in the layout. numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);
// Create the vertex input layout. result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &m_layout); if(FAILED(result)) { return false; }
// Release the vertex shader buffer and pixel shader buffer since they are no longer needed. vertexShaderBuffer->Release(); vertexShaderBuffer = 0;
pixelShaderBuffer->Release(); pixelShaderBuffer = 0; The final thing that needs to be setup to utilize the shader is the constant buffer. As you saw in the vertex shader, we currently have just one constant buffer so we only need to setup one here so we can interface with the shader. The buffer usage needs to be set to dynamic since we will be updating it each frame. The bind flags indicate that this buffer will be a constant buffer. The CPU access flags need to match up with the usage so it is set to D3D11_CPU_ACCESS_WRITE. Once we fill out the de ion, we can then create the constant buffer interface and then use that to access the internal variables in the shader using the function SetShaderParameters.
// Setup the de ion of the dynamic matrix constant buffer that is in the vertex shader. matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC; matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType); matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; matrixBufferDesc.MiscFlags = 0; matrixBufferDesc.StructureByteStride = 0;
// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer); if(FAILED(result)) { return false; }
return true; } ShutdownShader releases the four interfaces that were setup in the InitializeShader function.
void ColorShaderClass::ShutdownShader() { // Release the matrix constant buffer. if(m_matrixBuffer) { m_matrixBuffer->Release(); m_matrixBuffer = 0; }
// Release the layout. if(m_layout) { m_layout->Release(); m_layout = 0; }
// Release the pixel shader. if(m_pixelShader) { m_pixelShader->Release(); m_pixelShader = 0; }
// Release the vertex shader. if(m_vertexShader) { m_vertexShader->Release(); m_vertexShader = 0; }
return; } The OutputShaderErrorMessage writes out error messages that are generating when compiling either vertex shaders or pixel shaders.
void ColorShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename) { char* compileErrors; unsigned long long bufferSize, i; ofstream fout;
// Get a pointer to the error message text buffer. compileErrors = (char*)(errorMessage->GetBufferPointer());
// Get the length of the message. bufferSize = errorMessage->GetBufferSize();
// Open a file to write the error message to. fout.open("shader-error.txt");
// Write out the error message. for(i=0; iMap(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if(FAILED(result)) { return false; }
// Get a pointer to the data in the constant buffer. dataPtr = (MatrixBufferType*)mappedResource.pData;
// Copy the matrices into the constant buffer. dataPtr->world = worldMatrix; dataPtr->view = viewMatrix; dataPtr->projection = projectionMatrix;
// Unlock the constant buffer. deviceContext->Unmap(m_matrixBuffer, 0); Now set the updated matrix buffer in the HLSL vertex shader.
// Set the position of the constant buffer in the vertex shader. bufferNumber = 0;
// Finanly set the constant buffer in the vertex shader with the updated values. deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer);
return true; } RenderShader is the second function called in the Render function. SetShaderParameters is called before this to ensure the shader parameters are setup correctly.
The first step in this function is to set our input layout to active in the input assembler. This lets the GPU know the format of the data in the vertex buffer. The second step is to set the vertex shader and pixel shader we will be using to render this vertex buffer. Once the shaders are set, we render the triangle by calling the DrawIndexed DirectX 11 function using the D3D device context. Once this function is called it will render the green triangle.
void ColorShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount) { // Set the vertex input layout. deviceContext->IASetInputLayout(m_layout);
// Set the vertex and pixel shaders that will be used to render this triangle. deviceContext->VSSetShader(m_vertexShader, NULL, 0); deviceContext->PSSetShader(m_pixelShader, NULL, 0);
// Render the triangle. deviceContext->DrawIndexed(indexCount, 0, 0);
return; }
Modelclass.h
As stated previously the ModelClass is responsible for encapsulating the geometry for 3D models. In this tutorial we will manually setup the data for a single green triangle. We will also create a vertex and index buffer for the triangle so that it can be rendered.
//////////////////////////////////////////////////////////////////////////////// // Filename: modelclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _MODELCLASS_H_ #define _MODELCLASS_H_
////////////// // INCLUDES // ////////////// #include #include using namespace DirectX;
//////////////////////////////////////////////////////////////////////////////// // Class name: ModelClass //////////////////////////////////////////////////////////////////////////////// class ModelClass { private: Here is the definition of our vertex type that will be used with the vertex buffer in this ModelClass. Also take note that this typedef must match the layout in the ColorShaderClass that will be looked at later in the tutorial.
struct VertexType { XMFLOAT3 position; XMFLOAT4 color; };
public: ModelClass(); ModelClass(const ModelClass&); ~ModelClass(); The functions here handle initializing and shutdown of the model's vertex and index buffers. The Render function puts the model geometry on the video card to prepare it for drawing by the color shader.
bool Initialize(ID3D11Device*); void Shutdown(); void Render(ID3D11DeviceContext*);
int GetIndexCount();
private: bool InitializeBuffers(ID3D11Device*); void ShutdownBuffers(); void RenderBuffers(ID3D11DeviceContext*); The private variables in the ModelClass are the vertex and index buffer as well as two integers to keep track of the size of each buffer. Note that all DirectX 11 buffers generally use the generic ID3D11Buffer type and are more clearly identified by a buffer de ion when they are first created.
private: ID3D11Buffer *m_vertexBuffer, *m_indexBuffer; int m_vertexCount, m_indexCount; };
#endif
Modelclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: modelclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "modelclass.h" The class constructor initializes the vertex and index buffer pointers to null.
ModelClass::ModelClass() { m_vertexBuffer = 0; m_indexBuffer = 0; }
ModelClass::ModelClass(const ModelClass& other) { }
ModelClass::~ModelClass() { } The Initialize function will call the initialization functions for the vertex and index buffers.
bool ModelClass::Initialize(ID3D11Device* device) { bool result;
// Initialize the vertex and index buffers. result = InitializeBuffers(device); if(!result) { return false; }
return true; } The Shutdown function will call the shutdown functions for the vertex and index buffers.
void ModelClass::Shutdown() { // Shutdown the vertex and index buffers. ShutdownBuffers();
return; } Render is called from the ApplicationClass::Render function. This function calls RenderBuffers to put the vertex and index buffers on the graphics pipeline so the color shader will be able to render them.
void ModelClass::Render(ID3D11DeviceContext* deviceContext) { // Put the vertex and index buffers on the graphics pipeline to prepare them for drawing. RenderBuffers(deviceContext);
return; } GetIndexCount returns the number of indexes in the model. The color shader will need this information to draw this model.
int ModelClass::GetIndexCount() { return m_indexCount; } The InitializeBuffers function is where we handle creating the vertex and index buffers. Usually, you would read in a model and create the buffers from that data file. For this tutorial we will just set the points in the vertex and index buffer manually since it is only a single triangle.
bool ModelClass::InitializeBuffers(ID3D11Device* device) { VertexType* vertices; unsigned long* indices; D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc; D3D11_SUBRESOURCE_DATA vertexData, indexData; HRESULT result; First create two temporary arrays to hold the vertex and index data that we will use later to populate the final buffers with.
// Set the number of vertices in the vertex array. m_vertexCount = 3;
// Set the number of indices in the index array. m_indexCount = 3;
// Create the vertex array. vertices = new VertexType[m_vertexCount]; if(!vertices) { return false; }
// Create the index array. indices = new unsigned long[m_indexCount]; if(!indices) { return false; } Now fill both the vertex and index array with the three points of the triangle as well as the index to each of the points. Please note that I create the points in the clockwise order of drawing them. If you do this counter clockwise it will think the triangle is facing the opposite direction and not draw it due to back face culling. Always remember that the order in which you send your vertices to the GPU is very important. The color is set here as well since it is part of the vertex de ion. I set the color to green.
// Load the vertex array with data. vertices[0].position = XMFLOAT3(-1.0f, -1.0f, 0.0f); // Bottom left. vertices[0].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
vertices[1].position = XMFLOAT3(0.0f, 1.0f, 0.0f); // Top middle. vertices[1].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
vertices[2].position = XMFLOAT3(1.0f, -1.0f, 0.0f); // Bottom right. vertices[2].color = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
// Load the index array with data. indices[0] = 0; // Bottom left. indices[1] = 1; // Top middle. indices[2] = 2; // Bottom right. With the vertex array and index array filled out we can now use those to create the vertex buffer and index buffer. Creating both buffers is done in the same fashion. First fill out a de ion of the buffer. In the de ion the ByteWidth (size of the buffer) and the BindFlags (type of buffer) are what you need to ensure are filled out correctly. After the de ion is filled out you need to also fill out a subresource pointer which will point to either your vertex or index array you previously created. With the de ion and subresource pointer you can call CreateBuffer using the D3D device and it will return a pointer to your new buffer.
// Set up the de ion of the static vertex buffer. vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; vertexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the vertex data. vertexData.pSysMem = vertices; vertexData.SysMemPitch = 0; vertexData.SysMemSlicePitch = 0;
// Now create the vertex buffer. result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer); if(FAILED(result)) { return false; }
// Set up the de ion of the static index buffer. indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; indexBufferDesc.StructureByteStride = 0;
// Give the subresource structure a pointer to the index data. indexData.pSysMem = indices; indexData.SysMemPitch = 0; indexData.SysMemSlicePitch = 0;
// Create the index buffer. result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer); if(FAILED(result)) { return false; } After the vertex buffer and index buffer have been created you can delete the vertex and index arrays as they are no longer needed since the data was copied into the buffers.
// Release the arrays now that the vertex and index buffers have been created and loaded. delete [] vertices; vertices = 0;
delete [] indices; indices = 0;
return true; } The ShutdownBuffers function just releases the vertex buffer and index buffer that were created in the InitializeBuffers function.
void ModelClass::ShutdownBuffers() { // Release the index buffer. if(m_indexBuffer) { m_indexBuffer->Release(); m_indexBuffer = 0; }
// Release the vertex buffer. if(m_vertexBuffer) { m_vertexBuffer->Release(); m_vertexBuffer = 0; }
return; } RenderBuffers is called from the Render function. The purpose of this function is to set the vertex buffer and index buffer as active on the input assembler in the GPU. Once the GPU has an active vertex buffer it can then use the shader to render that buffer. This function also defines how those buffers should be drawn such as triangles, lines, fans, and so forth. In this tutorial we set the vertex buffer and index buffer as active on the input assembler and tell the GPU that the buffers should be drawn as triangles using the IASetPrimitiveTopology DirectX function.
void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext) { unsigned int stride; unsigned int offset;
// Set vertex buffer stride and offset. stride = sizeof(VertexType); offset = 0; // Set the vertex buffer to active in the input assembler so it can be rendered. deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
// Set the index buffer to active in the input assembler so it can be rendered. deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles. deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
return; }
Cameraclass.h
We have examined how to code HLSL shaders, how to setup vertex and index buffers, and how to invoke the HLSL shaders to draw those buffers using the ColorShaderClass. The one thing we are missing however is the view point to draw them from. For this we will require a camera class to let DirectX 11 know from where and also how we are viewing the scene. The camera class will keep track of where the camera is and its current rotation. It will use the position and rotation information to generate a view matrix which will be passed into the HLSL shader for rendering.
//////////////////////////////////////////////////////////////////////////////// // Filename: cameraclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _CAMERACLASS_H_ #define _CAMERACLASS_H_
////////////// // INCLUDES // ////////////// #include using namespace DirectX;
//////////////////////////////////////////////////////////////////////////////// // Class name: CameraClass //////////////////////////////////////////////////////////////////////////////// class CameraClass { public: CameraClass(); CameraClass(const CameraClass&); ~CameraClass();
void SetPosition(float, float, float); void SetRotation(float, float, float);
XMFLOAT3 GetPosition(); XMFLOAT3 GetRotation();
void Render(); void GetViewMatrix(XMMATRIX&);
private: float m_positionX, m_positionY, m_positionZ; float m_rotationX, m_rotationY, m_rotationZ; XMMATRIX m_viewMatrix; };
#endif The CameraClass header is quite simple with just four functions that will be used. The SetPosition and SetRotation functions will be used to set the position and rotation of the camera object. Render will be used to create the view matrix based on the position and rotation of the camera. And finally, GetViewMatrix will be used to retrieve the view matrix from the camera object so that the shaders can use it for rendering.
Cameraclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: cameraclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "cameraclass.h" The class constructor will initialize the position and rotation of the camera to be at the origin of the scene.
CameraClass::CameraClass() { m_positionX = 0.0f; m_positionY = 0.0f; m_positionZ = 0.0f;
m_rotationX = 0.0f; m_rotationY = 0.0f; m_rotationZ = 0.0f; }
CameraClass::CameraClass(const CameraClass& other) { }
CameraClass::~CameraClass() { } The SetPosition and SetRotation functions are used for setting up the position and rotation of the camera.
void CameraClass::SetPosition(float x, float y, float z) { m_positionX = x; m_positionY = y; m_positionZ = z; return; }
void CameraClass::SetRotation(float x, float y, float z) { m_rotationX = x; m_rotationY = y; m_rotationZ = z; return; } The GetPosition and GetRotation functions return the location and rotation of the camera to calling functions.
XMFLOAT3 CameraClass::GetPosition() { return XMFLOAT3(m_positionX, m_positionY, m_positionZ); }
XMFLOAT3 CameraClass::GetRotation() { return XMFLOAT3(m_rotationX, m_rotationY, m_rotationZ); } The Render function uses the position and rotation of the camera to build and update the view matrix. We first setup our variables for up, position, rotation, and so forth. Then at the origin of the scene we first rotate the camera based on the x, y, and z rotation of the camera. Once it is properly rotated when then translate the camera to the position in 3D space. With the correct values in the position, lookAt, and up we can then use the XMMatrixLookAtLH function to create the view matrix to represent the current camera rotation and translation.
void CameraClass::Render() { XMFLOAT3 up, position, lookAt; XMVECTOR upVector, positionVector, lookAtVector; float yaw, pitch, roll; XMMATRIX rotationMatrix;
// Setup the vector that points upwards. up.x = 0.0f; up.y = 1.0f; up.z = 0.0f;
// Load it into a XMVECTOR structure. upVector = XMLoadFloat3(&up);
// Setup the position of the camera in the world. position.x = m_positionX; position.y = m_positionY; position.z = m_positionZ;
// Load it into a XMVECTOR structure. positionVector = XMLoadFloat3(&position);
// Setup where the camera is looking by default. lookAt.x = 0.0f; lookAt.y = 0.0f; lookAt.z = 1.0f;
// Load it into a XMVECTOR structure. lookAtVector = XMLoadFloat3(&lookAt);
// Set the yaw (Y axis), pitch (X axis), and roll (Z axis) rotations in radians. pitch = m_rotationX * 0.0174532925f; yaw = m_rotationY * 0.0174532925f; roll = m_rotationZ * 0.0174532925f;
// Create the rotation matrix from the yaw, pitch, and roll values. rotationMatrix = XMMatrixRotationRollPitchYaw(pitch, yaw, roll);
// Transform the lookAt and up vector by the rotation matrix so the view is correctly rotated at the origin. lookAtVector = XMVector3TransformCoord(lookAtVector, rotationMatrix); upVector = XMVector3TransformCoord(upVector, rotationMatrix);
// Translate the rotated camera position to the location of the viewer. lookAtVector = XMVectorAdd(positionVector, lookAtVector);
// Finally create the view matrix from the three updated vectors. m_viewMatrix = XMMatrixLookAtLH(positionVector, lookAtVector, upVector);
return; } After the Render function has been called to create the view matrix, we can provide the updated view matrix to calling functions using this GetViewMatrix function. The view matrix will be one of the three main matrices used in the HLSL vertex shader.
void CameraClass::GetViewMatrix(XMMATRIX& viewMatrix) { viewMatrix = m_viewMatrix; return; }
Applicationclass.h
ApplicationClass now has the three new classes added to it. CameraClass, ModelClass, and ColorShaderClass have headers added here as well as private member variables. Remember that ApplicationClass is the main class that is used to render the scene by invoking all the needed class objects for the project.
//////////////////////////////////////////////////////////////////////////////// // Filename: applicationclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _APPLICATIONCLASS_H_ #define _APPLICATIONCLASS_H_
/////////////////////// // MY CLASS INCLUDES // /////////////////////// #include "d3dclass.h" #include "cameraclass.h" #include "modelclass.h" #include "colorshaderclass.h"
///////////// // GLOBALS // ///////////// const bool FULL_SCREEN = false; const bool VSYNC_ENABLED = true; const float SCREEN_DEPTH = 1000.0f; const float SCREEN_NEAR = 0.3f;
//////////////////////////////////////////////////////////////////////////////// // Class name: ApplicationClass //////////////////////////////////////////////////////////////////////////////// class ApplicationClass { public: ApplicationClass(); ApplicationClass(const ApplicationClass&); ~ApplicationClass();
bool Initialize(int, int, HWND); void Shutdown(); bool Frame();
private: bool Render();
private: D3DClass* m_Direct3D; CameraClass* m_Camera; ModelClass* m_Model; ColorShaderClass* m_ColorShader; };
#endif
Applicationclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: applicationclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "applicationclass.h" The first change to ApplicationClass is initializing the camera, model, and color shader objects in the class constructor to null.
ApplicationClass::ApplicationClass() { m_Direct3D = 0; m_Camera = 0; m_Model = 0; m_ColorShader = 0; }
ApplicationClass::ApplicationClass(const ApplicationClass& other) { }
ApplicationClass::~ApplicationClass() { } The Initialize function has also been updated to create and initialize the three new objects.
bool ApplicationClass::Initialize(int screenWidth, int screenHeight, HWND hwnd) { bool result;
// Create and initialize the Direct3D object. m_Direct3D = new D3DClass;
result = m_Direct3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR); if(!result) { MessageBox(hwnd, L"Could not initialize Direct3D", L"Error", MB_OK); return false; }
// Create the camera object. m_Camera = new CameraClass;
// Set the initial position of the camera. m_Camera->SetPosition(0.0f, 0.0f, -5.0f);
// Create and initialize the model object. m_Model = new ModelClass;
result = m_Model->Initialize(m_Direct3D->GetDevice()); if(!result) { MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK); return false; }
// Create and initialize the color shader object. m_ColorShader = new ColorShaderClass;
result = m_ColorShader->Initialize(m_Direct3D->GetDevice(), hwnd); if(!result) { MessageBox(hwnd, L"Could not initialize the color shader object.", L"Error", MB_OK); return false; }
return true; } Shutdown is also updated to shutdown and release the three new objects.
void ApplicationClass::Shutdown() { // Release the color shader object. if (m_ColorShader) { m_ColorShader->Shutdown(); delete m_ColorShader; m_ColorShader = 0; }
// Release the model object. if (m_Model) { m_Model->Shutdown(); delete m_Model; m_Model = 0; }
// Release the camera object. if (m_Camera) { delete m_Camera; m_Camera = 0; }
// Release the D3D object. if(m_Direct3D) { m_Direct3D->Shutdown(); delete m_Direct3D; m_Direct3D = 0; }
return; } The Frame function has remained the same as the previous tutorial.
bool ApplicationClass::Frame() { bool result;
// Render the graphics scene. result = Render(); if(!result) { return false; }
return true; } As you would expect the Render function had the most changes to it. It still begins with clearing the scene except that it is cleared to black. After that it calls the Render function for the camera object to create a view matrix based on the camera's location that was set in the Initialize function. Once the view matrix is created, we get a copy of it from the camera class. We also get copies of the world and projection matrix from the D3DClass object. We then call the ModelClass::Render function to put the green triangle model geometry on the graphics pipeline. With the vertices now prepared we call the color shader to draw the vertices using the model information and the three matrices for positioning each vertex. The green triangle is now drawn to the back buffer. With that the scene is complete and we call EndScene to display it to the screen.
bool ApplicationClass::Render() { XMMATRIX worldMatrix, viewMatrix, projectionMatrix; bool result;
// Clear the buffers to begin the scene. m_Direct3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
// Generate the view matrix based on the camera's position. m_Camera->Render();
// Get the world, view, and projection matrices from the camera and d3d objects. m_Direct3D->GetWorldMatrix(worldMatrix); m_Camera->GetViewMatrix(viewMatrix); m_Direct3D->GetProjectionMatrix(projectionMatrix);
// Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing. m_Model->Render(m_Direct3D->GetDeviceContext());
// Render the model using the color shader. result = m_ColorShader->Render(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix); if (!result) { return false; }
// Present the rendered scene to the screen. m_Direct3D->EndScene();
return true; }
| |
| меню : 1236 | https://habr.com/ru/articles/125356/
| |
| меню : 1233 |
Как делают игры | Все этапы создания игр - подробно
| |
| меню : 1231 | Введение
JavaScript предоставляет множество методов для работы с Document Object Model или сокращенно DOM (объектной моделью документа): одни из них являются более полезными, чем другие; одни используются часто, другие почти никогда; одни являются относительно новыми, другие признаны устаревшими.
Я постараюсь дать вам исчерпывающее представление об этих методах, а также покажу парочку полезных приемов, которые сделают вашу жизнь веб-разработчика немного легче.
Размышляя над подачей материала, я пришел к выводу, что оптимальным будет следование спецификациям с промежуточными и заключительными выводами, сопряженными с небольшими лирическими отступлениями.
Сильно погружаться в теорию мы не будем. Вместо этого, мы сосредоточимся на практической составляющей.
Для того, чтобы получить максимальную пользу от данной шпаргалки, пишите код вместе со мной и внимательно следите за тем, что происходит в консоли инструментов разработчика и на странице.
Вот как будет выглядеть наша начальная разметка:https://my-js.org/docs/cheatsheet/js-dom/
| |
|
меню : 1229 | Сидел я на днях, погруженный в унылую осеннюю тоску, и вспоминал прошлые дни. В прошлые дни все было лучше: свободное время не перерастало в скуку, хорошие идеи перерастали в хорошие программы. Короче говоря, в те дни я был молод и не работал. А еще я более-менее знал DirectX, и даже сделал симпатичную игру.
Ой, зря я начал так издалека. Попробуем снова.
Сидел я на днях, погруженный в унылую осеннюю скуку, и вспоминал прошлые дни. От тоски меня избавила идея разобраться в DirectX11. Загоревшись такой идеей, первым делом я попробовал найти уроки в интернете – безрезультатно. Тогда я открыл классический туториал MSDN. Вооружившись топором, я прорубил узкую тропу через его джунгли, снабжая код примеров густыми комментариями, а код статей – самодельными картинками и схемами. И вот. Дикая растительность повержена, длинный путь пройден, однако все эти сделанные мной штуковины могут кому-то пригодиться. Собственно, такая мысль и стала причиной рождения этого эпоса.
Все описанное ниже – очень вольный пересказ базового учебника Direct3D11 от Микрософт с сокращениями, пояснениями и дополнениями. Всего в MSDN насчитывается 7 уроков, которые я ужал до 6. Чтобы сравнять счет, пришлось составить собственный последний урок, который просто суммирует все описанное ранее (но в гораздо более интересной форме, поверьте).
Этот туториал не претендует на серьезность или, не дай бог, «остроумие» изложения. Я не пытаюсь тягаться с бессмертными творениями Antiloop’а. Цель одна – просто поделиться знаниями, которые накопились в моей голове, но мне самому вряд ли пригодятся.
Урок 1. Создание устройства DirectX
Давайте разберемся, что же нам пондобится. Кроме огромного желания сделать что-то крутое и обязательно трехмерное необходимо знание C++. Мы будем создавать окно при помощи WinAPI. Я использовал Visual C++ из Microsoft Visual Studio 2008, но в принципе версия C++ не играет роли. Вы можете взять любую другую среду. Кроме того, придется скачать с сайта Микрософт DirectX SDK. Ссылка найдется где-то здесь. После распаковки архива не забудьте через меню СервисaПараметры добавить каталоги с lib'ами и include'ами DirectX.
Итак, C++ выучен, SDK установлен, а идея будущей гениальной игры родилась. Что дальше? А дальше давайте разберемся, что такое вообще Direct3D. Как сообщает официальная справка, Direct3D – COM-библиотека, которая служит для облегчения и ускорения рисования в Windows. Direct3D напрямую работает с видеокартой, а это гораздо эффективнее, чем рисовать в стандартном окне при помощи стандартных API-функций. Кроме того, Direct3D сам проводит страшные трехмерные вычисления (ну или, во всяком случае, я так наивно думал, вспоминая DirectX8).
В этом уроке мы создадим минимально необходимые объекты Direct3D, привяжем их к нашему окну и научимся очищать его при помощи инструментов Direct3D. «Ха-ха», - скажете вы, - «очистить окно можно одной строчкой, и без всяких Иксов». Так оно и есть, но Микрософт довольно разумно сочла, что для начала осилить и такой объем информации – уже неплохо. Мы не будем идти против Стива Б. и Билла Г.
Прежде чем приступить к написанию собственно кода, давайте рассмотрим схему нашей программы.
Итак, сначала мы создаем окно. Это просто окно. Такое можно хоть в макросах Экселя создать, если подключить нужные API, так что код я приведу в конце. После этого нам надо будет создать устройства Direct3D, для чего мы отведем отдельную функцию. Кроме того, мы сделаем функцию удаления всех созданных устройств. Она будет вызываться при получении сообщения о завершении программы. В цикле сообщений мы используем время простоя (отсутствия сообщений), чтобы вызывать функцию Render(). Там-то мы средствами Direct3D и будем очищать наше окошко в восхитительный синий цвет.
// Главный цикл сообщений
MSG msg = {0};
while( WM_QUIT != msg.message ) {
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else // Если сообщений нет
Render(); // Рисуем
}
CleanupDevice(); // Удалить объекты Direct3D
Тут все понятно. Теперь давайте разберемся с создаваемыми объектами.
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;
D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pd3dDevice = NULL;
ID3D11DeviceContext* g_pImmediateContext = NULL;
IDXGISwapChain* g_pSwapChain = NULL;
ID3D11RenderTargetView* g_pRenderTargetView = NULL;
Пока можно расслабиться. Я подскажу, когда надо будет начинать думать. Переменные g_hInst и g_hWnd – идентификаторы нашего приложения и окна, их не будем вообще трогать. g_driverType понадобится для создания устройств Direct3D. Этот параметр указывает, производить вычисления в видеокарте или в центральном процессоре. Можно смело ставить на видеокарту, но Микрософт, как всегда, немного поиздевается над нами. g_featureLevel – параметр, указывающий, какую версию DirectX поддерживает наша видеокарта. Он, как и предыдущий, понадобится только для создания устройств Direct3D. Внимание, начинаем думать! Следующие три объекта работают в связке, они даже создаются одной функцией. Честно говоря, все три раньше были одним объектом D3DDevice, но программисты из Микрософт решили, что с новым поколением DirectX объекты должны размножаться. Боюсь представить, сколько их будет в 12 версии.
Давайте всмотримся внимательнее в объект-указатель на интерфейс ID3D11Device. Раньше девайс (устройство) использовался как для создания всяческих ресурсов (текстур, буферов трехмерных объектов, шейдеров и т. д.), так и для собственно рисования. Теперь его просто разрезали: созданием ресурсов занялся интерфейс ID3D11Device, а выводом графической информации – интерфейс ID3D11DeviceContext (контекст устройства, который Микрософт обозвали СобственноКонтекстом, а мы будем называть устройством рисования). Создавать IDXGISwapChain понадобилось для работы с буферами рисования и выводом нарисованного на экран. В любой программе будет присутствовать объект IDXGISwapChain, содержащий как минимум два буфера – задний (back buffer) и передний (front buffer). Передний буфер – это экран, точнее его часть внутри нашего окна. На заднем буфере мы в DirectX отрисовываем сцену, и, когда она готова, мы вызываем функцию g_pSwapChain->Present(…), которая копирует задний буфер в передний, т. е. показывает на экране все тщательно и любовно нарисованное.
Конечно, тут выползает вопрос: а зачем эта муть с двумя буферами? Почему бы сразу не рисовать на экран? Если такой вопрос не возник, или вы уже догадались – можете пропустить абзац. Давайте пофантазируем. Пусть мы пишем крутую игру, в которой злобный монстр, сверкая кровожадными глазами, рыщет вдоль стены замка на фоне сумеречного неба. Сначала мы расправимся с небом: нарисуем какую-нибудь сферу, обтянутую текстурой облаков. Потом нарисуем травку, потом стену замка и, наконец, красноглазого красавца. Вполне очевидно, что трава закроет собой часть неба, стена – часть неба и травы и монстр – часть стены. Представили, что будет на экране, если рисовать прямо на нем? А ведь рисовать придется десятки раз в секунду! И каждый раз на экране будут мелькать облака, стена и травка, которых не должно быть видно. У любого игрока разболятся глаза, он в панике выключит игру, и никто так и не увидит, как красиво монстр разрывает врагов на куски.
Последний объект – g_pRenderTargetView. Не пугайтесь, тут нет ничего сложного. Это и есть объект нашего заднего буфера, в котором мы будем рисовать свой трехмерный мир.
Перейдем к важным местам из функции InitDevice(), которая займется созданием и инициализацией только что изученных объектов. Вот так мы можем создать связку трех основных объектов:
// Структура, описывающая цепь связи (Swap Chain)
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof( sd ) ); // очищаем ее
sd.BufferCount = 1; // у нас один буфер
sd.BufferDesc.Width = 320; // ширина буфера
sd.BufferDesc.Height = 210; // высота буфера
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // формат пикселя буфере
sd.BufferDesc.RefreshRate.Numerator = 75; // частота обновления экрана
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // назначение буфера
sd.OutputWindow = hWnd // привязываем к нашему окну
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE; // не полноэкранный режим
D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, NULL, &g_pImmediateContext);
Сначала мы описываем передний буфер. sd.BufferDesc.Format установлен как DXGI_FORMAT_R8G8B8A8_UNORM. Это означает, что для каждого пикселя в буфере будут храниться 4 значения: красный, зеленый, синий компоненты цвета и альфа-канал, все по 8 бит. В DirectX вообще много всяких буферов, поэтому буферы, используемые для рисования, в Микрософте решили назвать Видами. Понятия не имею, почему именно видами. А вот передний буфер назвали буфером вывода. Поэтому флаг DXGI_USAGE_RENDER_TARGET_OUTPUT указывает, что буфер является целевым буфером для вывода графической информации, т. е. передним буфером. Название флага так и переводится: «использовать как целевой буфер для вывода». Наконец, мы вызываем функцию D3D11CreateDeviceAndSwapChain(...), которая по нашему описанию создает все устройства.
Ну вот, пока ничего сложного! Но теории получилось слишком много, поэтому остальное давайте рассмотрим по ходу написания программы. Нас еще ждет создание заднего буфера, функция рисования, а перед всем этим – обещанная пакость от Микрософт.
Для начала создайте пустой проект C++ Win32 под названием Urok1, добавьте в него файл Urok1.cpp и какую-нибудь иконку в ресурсы. У меня это значок DirectX'а, но можно что угодно впихнуть, хоть значок OpenGL – никто не обидится.
Теперь код:
//--------------------------------------------------------------------------------------
// Урок 1. Создание устройств Direct3D11. Основан на примере из SDK (c) Microsoft Corp.
//--------------------------------------------------------------------------------------
#include
#include
#include
#include "resource.h"
//--------------------------------------------------------------------------------------
// Глобальные переменные
//--------------------------------------------------------------------------------------
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;
D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
ID3D11Device* g_pd3dDevice = NULL; // Устройство (для создания объектов)
ID3D11DeviceContext* g_pImmediateContext = NULL; // Контекст устройства (рисование)
IDXGISwapChain* g_pSwapChain = NULL; // Цепь связи (буфера с экраном)
ID3D11RenderTargetView* g_pRenderTargetView = NULL; // Объект заднего буфера
//--------------------------------------------------------------------------------------
// Предварительные объявления функций
//--------------------------------------------------------------------------------------
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ); // Создание окна
HRESULT InitDevice(); // Инициализация устройств DirectX
void CleanupDevice(); // Удаление созданнных устройств DirectX
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); // Функция окна
void Render(); // Функция рисования
//--------------------------------------------------------------------------------------
// Точка входа в программу. Здесь мы все инициализируем и входим в цикл сообщений.
// Время простоя используем для вызова функции рисования.
//--------------------------------------------------------------------------------------
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
UNREFERENCED_PARAMETER( hPrevInstance );
UNREFERENCED_PARAMETER( lpCmdLine );
if (FAILED(InitWindow(hInstance, nCmdShow)))
return 0;
if (FAILED(InitDevice()))
{
CleanupDevice();
return 0;
}
// Главный цикл сообщений
MSG msg = {0};
while( WM_QUIT != msg.message )
{
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else // Если сообщений нет
{
Render(); // Рисуем
}
}
CleanupDevice();
return ( int )msg.wParam;
}
Здесь мы все уже рассматривали. При запуске программы создаем окно и устройства Direct3D, а затем входим в цикл обработки сообщений. Теперь еще довольно большой скучный кусок, который создает окно и обрабатывает системные сообщения. Весь код стандартный, без изменений. Так же выглядит создание окна на ассемблере.
//--------------------------------------------------------------------------------------
// Регистрация класса и создание окна
//--------------------------------------------------------------------------------------
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow )
{
// Регистрация класса
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor (NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"Urok01WindowClass";
wcex.hIconSm = LoadIcon (wcex.hInstance, (LPCTSTR)IDI_ICON1);
if (!RegisterClassEx(&wcex))
return E_FAIL;
// Создание окна
g_hInst = hInstance;
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect (&rc, WS_OVERLAPPEDWINDOW, FALSE);
g_hWnd = CreateWindow (L"Urok01WindowClass", L"Урок 1: Создание устройств Direct3D", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL );
if (!g_hWnd)
return E_FAIL;
ShowWindow (g_hWnd, nCmdShow);
return S_OK;
}
//--------------------------------------------------------------------------------------
// Вызывается каждый раз, когда приложение получает сообщение
//--------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
PAINTSTRUCT ps;
HDC hdc;
switch( message )
{
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
EndPaint( hWnd, &ps );
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
default:
return DefWindowProc( hWnd, message, wParam, lParam );
}
return 0;
}
Создавть API-шное окно на самом деле вовсе не обязательно. Можно и в проект MFC засунуть код DirectX, но зачем нам тащить с собой горы лишних скучных файлов? Рассмотрим долгожданную функцию создания Direct3D. На ней мы немного замедлимся, чтобы мозги не вскипели.
HRESULT InitDevice()
{
HRESULT hr = S_OK;
RECT rc;
GetClientRect( g_hWnd, &rc );
UINT width = rc.right - rc.left; // получаем ширину
UINT height = rc.bottom - rc.top; // и высоту окна
UINT createDeviceFlags = 0;
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
UINT numDriverTypes = ARRAYSIZE( driverTypes );
С началом все понятно: мы при помощи функции GetClientRect(…) получаем координаты нашего окна и вычисляем его ширину и высоту. Они понадобятся позднее. Потом создается массив D3D_DRIVER_TYPE. Такое значение требуется указать при создании устройств Direct3D. Но мы не знаем заранее, поддерживается ли на компьютере хардварная обработка 3D (хотя на самом деле знаем: поддерживается). Поэтому Микрософт запихала возможные значения в массив в порядке убывания их желательности для нас и возрастания вероятности того, что система их поддерживает (ага, вероятность возрастает с 99,9% до 100). Вообще, Микрософт создает учебные примеры таким образом, что код, который можно написать одной строчкой, превращается в десять. Ниже мы пройдемся по этому массиву и будем пытаться создать устройство. Если пройдет ошибка, пробуем следующий тип. Всё примитивно, но поскольку сейчас все компьютеры поддерживают хардварную обработку, спрашивается, зачем такая перестраховка в учебном примере?
// Тут мы создаем список поддерживаемых версий DirectX
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
UINT numFeatureLevels = ARRAYSIZE( featureLevels );
// Сейчас мы создадим устройства DirectX. Для начала заполним структуру,
// которая описывает свойства переднего буфера и привязывает его к нашему окну.
DXGI_SWAP_CHAIN_DESC sd; // Структура, описывающая цепь связи (Swap Chain)
ZeroMemory( &sd, sizeof( sd ) ); // очищаем ее
sd.BufferCount = 1; // у нас один задний буфер
sd.BufferDesc.Width = width; // ширина буфера
sd.BufferDesc.Height = height; // высота буфера
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // формат пикселя в буфере
sd.BufferDesc.RefreshRate.Numerator = 75; // частота обновления экрана
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // назначение буфера - задний буфер
sd.OutputWindow = g_hWnd; // привязываем к нашему окну
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE; // не полноэкранный режим
А вот список поддерживаемых версий действительно необходим. Кому нужна игра, которая перестанет запускаться при выходе обновленной версии DirectX? Если она запускается в 10 версии, то в 11-ой точно должна работать. Структуру с описанием переднего буфера мы уже рассматривали. Только теперь мы указываем в ней размеры нашего окна.
for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
g_driverType = driverTypes[driverTypeIndex];
hr = D3D11CreateDeviceAndSwapChain ( NULL, g_driverType, NULL, createDeviceFlags, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );
if (SUCCEEDED(hr)) // Если устройства созданы успешно, то выходим из цикла
break;
}
if (FAILED(hr)) return hr;
Как и обещано, в цикле проходимся по массиву driverTypes. Вуаля! Все три устройства создаются одной строчкой. Но это не все. Нам ведь надо куда-то рисовать. А куда? Правильно, в задний буфер.
// Теперь создаем задний буфер. Обратите внимание, в SDK
// RenderTargetOutput - это передний буфер, а RenderTargetView - задний.
ID3D11Texture2D* pBackBuffer = NULL;
hr = g_pSwapChain->GetBuffer( 0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer );
if (FAILED(hr)) return hr;
// Я уже упоминал, что интерфейс g_pd3dDevice будет
// использоваться для создания остальных объектов
hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );
pBackBuffer->Release();
if (FAILED(hr)) return hr;
// Подключаем объект заднего буфера к контексту устройства
g_pImmediateContext->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL );
Не пугайтесь! Все кажется непонятным, но это только благодаря злодейскому замыслу Микрософта. В первой строчке создается объект текстуры. «Серьезно, текстуры?» - скажете вы. – «Что мы собрались покрывать текстурой, если еще даже рисовать негде?» На самом деле все очень умно. В Direct3D текстура – это не какое-то изображение, а просто область памяти. Память эту можно использовать в разных целях. Точнее, в трех. Как буфер для рисования, как буфер глубин и как собственно текстуру. Второй и третий случаи мы рассмотрим немного позже. Поэтому первой строчкой мы просто создали указатель на объект буфера. Вторая строчка загружает из объекта g_pSwapChain характеристики буфера. Он ведь должен точь-в-точь соответствовать переднему, да? Теперь давайте вспомним об объекте g_pRenderTargetView. Не зря же мы объявили его в самом начале. Он создается при помощи девайса по характеристикам, загруженным, как уже сказано, из g_pSwapChain. Напоминаю, через контекст мы будем рисовать. Поэтому в конце необходимо подключить созданный задний буфер к нему. Фффу, с этим разделались.
// Настройка вьюпорта
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)width;
vp.Height = (FLOAT)height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
// Подключаем вьюпорт к контексту устройства
g_pImmediateContext->RSSetViewports (1, &vp);
Еще один подарочек от Микрософта. Раньше вьюпорт всегда устанавливался по умолчанию при создании девайса, и не возникало необходимости инициализировать его самостоятельно. Здесь мы просто указываем, что верхний левый угол окна у нас имеет координаты (0, 0), а ширина и высота соответствуют ширине и высоте окна. Еще мы настраиваем масштаб буфера глубины. Просто пока не обращайте на это внимания. Вот теперь точно все.
return S_OK;
}
Осталось совсем немножко. Так выглядит функция очистки памяти:
//--------------------------------------------------------------------------------------
// Удалить все созданные объекты
//--------------------------------------------------------------------------------------
void CleanupDevice()
{
// Сначала отключим контекст устройства, потом отпустим объекты.
if( g_pImmediateContext ) g_pImmediateContext->ClearState();
// Порядок удаления имеет значение. Обратите внимание, мы удалеям
// эти объекты порядке, обратном тому, в котором создавали.
if( g_pRenderTargetView ) g_pRenderTargetView->Release();
if( g_pSwapChain ) g_pSwapChain->Release();
if( g_pImmediateContext ) g_pImmediateContext->Release();
if( g_pd3dDevice ) g_pd3dDevice->Release();
}
Пояснять просто нечего. И то, ради чего мы столько возились: функция рендеринга.
//--------------------------------------------------------------------------------------
// Рисование кадра
//--------------------------------------------------------------------------------------
void Render()
{
// Просто очищаем задний буфер
float ClearColor[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; // красный, зеленый, синий, альфа-канал
g_pImmediateContext->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
// Выбросить задний буфер на экран
g_pSwapChain->Present( 0, 0 );
}
Ну очень просто. 1. Создаем цвет, которым будем очищать задний буфер. 2. Очищаем задний буфер (напоминаю: мы всегда рисуем в буфере при помощи контекста, для этого он и создавался). 3. Показываем задний буфер зрителям (еще напоминание: SwapChain у нас как раз и занимается связью буфера с экраном)!
Готово! Компилируем… Ошибка. И правильно. Заходим в свойства проекта, Свойства конфигурацииaКомпоновщикaВвод. В «Дополнительные зависимости» вставляем «d3d11.lib d3dcompiler.lib d3dx11d.lib d3dx9d.lib dxerr.lib dxguid.lib winmm.lib comctl32.lib» (без кавычек). Теперь должно скомпоноваться нормально.
Быстрее запускаем и наслаждаемся восхитительным темно-синим цветом – пожалуй, лучшей находкой Микрософта со времен DX8… упс! А вот нет! Из тоски по старым временам я заменил цвет на тот самый классический жутко-синий, который так манил меня много лет назад. В моих уроках вам придется смириться с этим цветом. Хотите насладиться микрософтовским – просто откройте любой пример из их туториала.
| |
| меню : 1228 |  1
|  Часть 1.1 — OpenGL Вступление Прежде чем мы начнем наше путешествие нам стоило бы разобраться что такое OpenGL. В основном под OpenGL понимают API (Интерфейс Программирования Приложений), который предоставляет большой набор функций, которые мы можем использовать для управления графикой и изображениями. Но на самом деле OpenGL это скорее спецификация, разработанная и поддерживаемая Khronos Group. Спецификация OpenGL описывает каким будет результат выполнения каждой конкретной функции и что она должна делать. А уже реализация этих спецификаций лежит на плечах разработчиков. И поскольку спецификация не описывает детали реализации, соответственно имеют право на существование различные реализации OpenGL, по крайней мере пока они соответствуют спецификациям. Люди, разрабатывающие OpenGL библиотеки, зачастую, являются производителями видеокарт. Каждая видеокарта, которую вы покупаете, поддерживает конкретные версии OpenGL из набора библиотек, разработанных для данной серии видеокарт. При использовании Apple системы, OpenGL библиотеки поддерживаются Apple, под Linux существуют комбинации версий от поставщиков и пользовательских адаптаций этих библиотек. Это также означает, что если используемая вами версия OpenGL показывает странное поведение, значит, с большой вероятностью — это ошибка производителей видеокарт. Так как большинство реализаций разрабатываются производителями видеокарт, для исправления багов требуется обновить драйвера видеокарты. Это одна из причин, почему почти все уроки рекомендуют обновлять драйвера на видеокарту. Khronos выложила в публичный доступ все спецификации для всех версий OpenGL. Заинтересовавшийся читатель может найти спецификации OpenGL 3.3 (именно эту версию OpenGL мы будем использовать) здесь. Спецификации отлично показывают правила работы всех функций. Core-profile и Immediate mode (Мгновенный режим) Раньше, использование OpenGL предполагало разработку в Immediate mode (также известен как фиксированный конвейер (fixed function pipeline)), которая была проста в использовании для рисования графики. Большинство функционала OpenGL было скрыто в библиотеках и у разработчиков не было свободы в понимании вычислений, производимых OpenGL. Разработчики требовали большей гибкости в разработке и позже спецификация стала более гибкой, а разработчики получили больше контроля над процессом отрисовки их графики. Immediate mode был прост в использовании и понимании, но он был крайне неэффективным. По этой причине спецификация указала Immediate mode как устаревший, и начиная с версии 3.2 начала мотивировать программистов использовать Core-profile режим, который исключал весь устаревший функционал. При использовании core-profile, OpenGL заставляет нас пользоваться современными практиками. Когда мы пытаемся использовать устаревшие функции, OpenGL выбрасывает ошибку и прекращает отрисовку. Преимущества использования современных практик — это гибкость и эффективность, но к сожалению бОльшая сложность в изучении. Immediate mode является бОльшей абстракцией и он скрывает большое количество реальной работы, выполняемой OpenGL и поэтому его было легко изучать, но трудно разобраться, как OpenGL на самом деле работает. Современный подход требует от разработчика полного понимания OpenGL и графического программирования в целом и хоть это немного сложнее, такая схема позволяет добиться большей гибкости, эффективности. Это причина, почему наши уроки основаны на Core-Profile OpenGL версии 3.3. Хоть он немного и сложнее, но это того стоит. Сейчас уже вышли гораздо более новые версии OpenGL (на момент написания 4.5) и вы можете спросить: зачем мы должны изучать OpenGL 3.3, когда уже вышел 4.5? Ответ довольно прост. Все старшие версии OpenGL, начиная от версии 3.3 не добавляют различные полезные возможности без изменения основной механики. Новые версии просто предоставляют немного более эффективные или более удобные способы выполнения одних и тех же операций. В результате все концепты и техники, применимые к OpenGL 3.3 можно применить к новым версиям OpenGL. Использование новейших версий OpenGL сопряжено с одной проблемой. Исполнять новейшие API смогут только современные видеокарты. Расширения Отличной возможностью OpenGL является поддержка расширений. В то время, когда производители видеокарт представляют новую технологию или новую обширную оптимизацию для отрисовки, в драйверах появляется расширение, относящееся к этому событию. Если аппаратное обеспечение, на котором запущено приложение, поддерживает расширение, значит разработчик может использовать функционал, предоставляемый этим расширением для более продвинутой, или эффективной отрисовки графики. Таким образом графический программист может использовать новые технологии без ожидания их реализация в новых версиях OpenGL, просто проверив поддержку технологии видеокартой. Зачастую, если какое-то расширение пользуется большим спросом, его реализуют как часть следующей версии OpenGL. Разработчику надо лишь проверить доступность расширения (или использовать библиотеку расширения). Такой подход позволяет программисту выполнять действия более эффективно, основываясь на имеющихся у него расширениях: if(GL_ARB_extension_name) { // Можно использовать новый функционал. Он поддерживается железом } else { // Расширение не поддерживается: делаем по-старинке. } C OpenGL 3.3 нам редко будут нужны расширения, но когда будут нужны, необходимые инструкции будут предоставлены. Конечный автомат OpenGL по своей сути — это большой конечный автомат: набор переменных, определяющий поведение OpenGL. Под состоянием OpenGL в основном имеется ввиду контекст OpenGL. В процессе использования OpenGL, мы часто изменяем состояния, устанавливая некоторых опции, управляем буферами, а затем отрисовываем, используя текущий контекст. Когда мы говорим OpenGL, что мы хотим начать отрисовывать, к примеру, линии, вместо треугольников, то мы меняем состояние OpenGL, изменяя опцию, отвечающую за то как OpenGL должен рисовать. После изменения состояния OpenGL, на отрисовку линий, все последующие функции отрисовки будут отрисовывать линии вместо треугольников. Во время работы с OpenGL мы будем проходить через несколько меняющих состояния функций, которые будут менять контекст, и через несколько меняющий состояния функций, выполняющие действия в зависимости от текущего состояния OpenGL. До тех пор, пока вы держите в голове тот факт, что OpenGL — это большой конечный автомат, большинство функционала будет вам понятна. Объекты Библиотеки OpenGL написаны на C и имеют множественные ответвления, но в основном это C библиотека. Поскольку большинство конструкций из языка C не транслируется в высокоуровневые языки OpenGL был разработан с использованием большого количества абстракций. Одной из таких абстракций является система объектов в OpenGL. Объект в OpenGL — это набор опций, которые представляют подмножество состояний OpenGL. К примеру мы можем создать объект, описывающий конфигурацию отрисовки окна; мы можем задать размер, количество цветов и так далее. Такой объект можно представить C-подобной структурой: struct object_name { GLfloat option1; GLuint option2; GLchar[] name; }; Примитивные типы Заметьте, что при использовании OpenGL рекомендуется использовать примитивы, заданные OpenGL. Вместо использования float записывать его с приставной GL. Тоже самое для int, uint char, bool и так далее. OpenGL определяет разметку памяти для его GL примитивов для обеспечения кроссплатформенности, поскольку некоторые операционные системы могут иметь иную разметку. Использования OpenGL примитивов позволяет добиться полной кроссплатформенности вашего приложения. Каждый раз, когда мы хотим использовать объекты в основном мы запишем это как-то так: // OpenGL состояние struct OpenGL_Context { ... object* object_Window_Target; ... }; // Создание объекта GLuint objectId = 0; glGenObject(1, &objectId); // Привязка объекта к контексту glBindObject(GL_WINDOW_TARGET, objectId); // Установка значений опции объекта, привязанного к GL_WINDOW_TARGET glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800); glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600); // Установка цели контекста в значение по умолчанию glBindObject(GL_WINDOW_TARGET, 0); Этот небольшой участок кода — то, что вы будете часто встречать во время работы с OpenGL. В начале мы создаем объект и сохраняем ссылку на него в виде идентификационного номера (id). (Реальные данные объекта спрятаны в реализации). Затем мы привязываем объект к требуемой части контекста (Расположение целевого объекта окна из примера задано, как `GL_WINDOW_TARGET`). Затем мы устанавливаем значения опций окна и, в конце концов, отвязываем объект, установив id в 0. Значения, установленные нами продолжают храниться в объекте, доступ к которому мы можем получить через objectId и восстановить их снова привязав объект к GL_WINDOW_TARGET. Данный код лишь показывает пример того, как работает OpenGL. В последствии будут представлены реальные примеры. Основная фишка этих объектов состоит в том, что мы можем объявлять множество объектов в нашем приложении, задавать их опции и когда бы мы не запускали операции с использованием состояния OpenGL мы можем просто привязать объект с нашими предпочитаемыми настройками. К примеру этом могут быть объекты с данными 3D модели или нечто, что мы хотим на этой модели отрисовать. Владение несколькими объектами позволяет просто переключаться между ними в процессе отрисовки. Давайте начнем Теперь вы немного узнали про OpenGL как о спецификации, так и о библиотеке. Узнали примерный алгоритм работы и несколько особенностей, используемых OpenGL. Не расстраивайтесь, если что-то недопоняли, далее мы пошагово пройдемся по всем этапам и вы увидите достаточно примеров, чтобы разобраться во всех хитросплетениях OpenGL. Если вы уже готовы начать — то мы можем начать создавать OpenGL контекст и наше первое окно прямо тут. |
| меню : 1221 |
GameEngine DirectX11
| |
| меню : 1220 |
https://vk.com/video/@kinobro?z=video-220018529_456239632%2Fclub220018529%2Fpl_-220018529_-2
| |
|
меню : 1219 |  1
|  https://vk.com/video-220018529_456239561 |
| меню : 1218 |  1
|  ЭТАЛОН СТРАТЕГИЙ В РЕАЛЬНОМ ВРЕМЕНИ.
СТРАТЕГИЯ В РЕАЛЬНОМ ВРЕМЕНИ • БЕСПЛАТНАЯ ИГРА
https://starcraft2.blizzard.com/ru-ru/ |
| меню : 1203 |  5
|  Продам помещение город Колпашево , 22.5 кв.м. центр города. По адресу: Белинского 9. Тел. 8-953-924-91-71. |
| меню : 1201 | Всех читателей этого форума приветствую! Наша формирующаяся студия занимается разработкой веб игр, с последующей точкой роста в виде выхода на мобильные платформы. В данный момент в разработке 2 игры, в жанрах гонки и шутер. Стиль: стилизованный реализм и лоуполи Используем движок Unity(C#), система контроля версий - GitHub В студии над играми трудятся 3д художники, программисты, 2д художники, UI/UX художник, продюсер
На данный момент мы подходим к концу в реализации механик и оптимизации различных ассетов, а также их создание с нуля. Готовы механики, контент, базовый UI
| |
|
меню : 1200 |  1
| |
| меню : 1197 |
c# treeview
| |
| меню : 1193 |
Гузель Хасанова - Не плачь | Official Audio | 2021
| |
| меню : 1192 |
How to use c# dll in c++
| |
|
| меню : 1186 |
Idiots Around Europe | Try Not To Laugh 2023
| |
| меню : 1182 |  8
|  Продам большой земельный участок 2668 к.м рядом с центром города на участке два нежилых здания.
Колпашево, Горького 15
Тел. 8-953-924-91-71
|
| меню : 1181 |  1
|  Продам мелкие огурчики для засолки 5литров 500руб.Тел:89539112923 |
|
меню : 1180 |
Какой ПРЕДЕЛ ВЫСОТЫ и РАЗМЕРОВ? Закон Квадрата-куба — ТОПЛЕС
| |
| меню : 1178 |  1
| |
| меню : 1176 |  1
| |
| меню : 1175 |  1
|  Продам подростковый велосипед, так же подойдёт для взрослых. Колеса стоят на 24
Состояние хорошее
Цена 4500 руб
Вопросы в личку или тел 89138225747
|
|
меню : 1174 |  1
|  Продам комод б/у в хорошем состоянии. Размеры: Ш 80/ Г 48/ В 90. Четыре вместительных ящика и отделение с полочками. Для хранения вещей, одежды, игрушек и т.д. Цена 3000 р. |
| меню : 1173 |  1
| |
| меню : 1172 | Семья с 2 детьми, снимет 2-3 комнатную квартиру в геологе, районе ул. Лазо,ул. Мира, Кирова. Платежеспособные, адекватные. На длительный срок. Можно без мебели. 89996191784
| |
| меню : 1171 | Какое масло лучше всего заливать в тойоту каролу 1.8л
| |
|
меню : 1170 | https://setom.ru/?v255
| |
| меню : 1169 |
Приключения Электроника (1979) комедия
| |
| меню : 1165 |
Гостья из будущего все серии подряд
| |
| меню : 1160 |  3
|  Продам рассаду: Помидоры, баклажанны, перец, капуста, кабачки, дыни. Петуния Изи вейв белая, Петуния Игл пик вэйн, петуния мультифлора серия “Дот стар”, Петуния мини многоцветковая минибело “Сакура F1” (НОВИНКА), Петуния Шок вейв F1 Дип перпл, Петуния Гавриш многоцветковая F1 Золотая рыбка, Петуния Софистика лайм биколор, Петуния Лизетта F1 каскадно - ампельная, Петуния Жёлтая звезда F1 Каскадная лавина цветов Петуния Оригинальной окраски, Петуния Многоцветковая “АЛЬДЕРМАН” яркое и продолжительное цветение, Петуния Превосходнейшая F1 “РОЗЕА” Петуния “ПИККОЛИНО” F1 красная, Крупноцветковая ДЕДИ F1 Лиловая, Сальвия сверкающая “КОСТЁР”, Голландская лилия Санвиталия ОРАНЖЕВАЯ Лобелия Ампельная “КАСКАД Вайт”, Лобелия “НОСТАЛЬЖИ”, Лобелия “АМПЕЛЬНАЯ КАСКАД” идеальна для подвесных корзин и бордюров. Лобелия “БИСЕР ГОЛУБОЙ” Гацания “МУЗА” Приехать и выбрать можно по адресу: город Колпашево пер.Коммунальный 5. Предварительный звонок по телефону 8-953-911-29-23 Наталья. Возможна ДОСТАВКА. |
|
меню : 1157 |
30 САМЫХ ОЖИДАЕМЫХ ФИЛЬМОВ 1-ОЙ ПОЛОВИНЫ 2023. ТОП-30 ФИЛЬМОВ 2023
| |
| меню : 1071 |
РЖАЧ // Путин президент Украины // тайная жизнь Путина
| |
| меню : 1064 |  1
|  Продам рассаду баклажан. Тел:89539112923 |
| меню : 1063 |  1
| |
|
меню : 1061 |  1
| |
| меню : 1060 |  1
| |
| меню : 1058 |  1
| |
| меню : 1057 |
В Колпашеве стартовала череда субботников
| |
|
меню : 1056 |
Депутаты районной Думы на очередном заседании рассмотрели 13 вопросов
| |
| меню : 1055 |  1
|  Продам рассаду лобелии. Тел:89539112923 |
| меню : 1053 |
МЧС напоминают, выход на весенний лёд может стоить жизни
| |
| меню : 1052 |  1
| |
|
меню : 1051 |
35 САМЫХ ТРОГАТЕЛЬНЫХ МОМЕНТОВ, СНЯТЫХ НА КАМЕРУ
| |
| меню : 1048 |  1
|  По оперативной информации наблюдаются подвижки ледовых масс на р.Обь в с.Могочино (Молчановский район), что может привести к резкому подъему воды в реке и затоплению береговой линии, в связи с чем, просим собственников позаботиться о безопасности транспортных средств, находящихся на левом берегу р.Обь в г.Колпашево. |
| меню : 1044 | Общая прибыль ведущих авиакомпаний в прошлом году достигла рекордных ₽88 млрд во многом благодаря субсидиям. Отрасль нуждается в аналогичной поддержке и в текущем году, но в Минфине не получали мотивированных обоснований для этого.
| |
| меню : 1040 | https://chat.whatsapp.com/JgDpklrXdn5DWRMzuSvPdP
| |
|
меню : 1003 |  1
| |
| меню : 987 |  1
|  Пресс-секретарь президента РФ Дмитрий Песков заявил, что НАТО продолжает стремиться включить в свой состав Украину, что подтверждает правильность решения России о начале там военной операции. С таким заявлением представитель Кремля выступил в ответ на реплику генсека североатлантического альянса Йенса Столтенберга о том, что все страны-участницы ждут вступления Украины. |
| меню : 955 |
Как разместить ссылку на видео на нашем сайте setom.ru
| |
| меню : 954 |
Колпашево Геолог
| |
|
меню : 953 |
Поздравления с новым годом Лёши !
| |
| меню : 948 |  1
|  Пиломатериал в наличии и под заказ. 89528051212 |
| меню : 946 |  4
| |
| меню : 936 | Срочников после прохождения дополнительной подготовки в миротворческом контингенте смогут задействовать для выполнения «упрощенных задач», они могут быть водителями и поварами, сообщил Ura.ru глава комитета Госдумы по обороне Андрей Картаполов. Сейчас в них могут участвовать добровольно только контрактники. В Думе пообещали, что служащие по призыву будут выполнять задачи не высшей сложности.
| |
|
меню : 931 |  3
| |
| меню : 914 |
roblox mad city
| |
| | меню : 909 |  1
| |
|
меню : 907 |
map Array Method | JavaScript Tutorial
| |
| меню : 900 |  1
| |
| меню : 899 |  1
| |
| меню : 898 |  1
|  Куплю лом чёрных и цветных металлов. Заберу с места. Чермет от 500 кг. ( Сделаем демонтаж любой сложности). 8-923-421-74-76
Старую бытовую технику СССР.
Советские стиральные машинки, холодильники, морозильные лари, электродвигателя, сварочные аппараты.
Легковые, грузовые, водный транспорт.
Делаем справки об утилизации.
На лом чёрных и цветных металлов.
Цены цветмет
Медь микс 420
Медь лужёная и тонкая пережёная(труха) - 15% от микса
Медь блеск 460
Медь шина 450
Медь кусок 420
Латунь 200
Лат.волос -15% о микса
Радиат. 200
Ал.втор. 50
Ал.перв. 70
Ал.пища 70
Ал.шлам 18
Ал.фольга 25
Банка 35
Цам 45
Амг 25
Акб пласт 32
Акб эбанит 27
Акб гелий 25
Свинец каб.58
Свинец кусок 50
Свинец грузики 26
Лат.стр. 160
Медн.стр. 270
Нерж. 10% 30
Нерж. 8% 25
Платы оргтехники 18 |
|
меню : 894 |  1
| |
| меню : 893 |
Enlisted
| |
| меню : 859 |
Parit Ukraina Jadi Bulan-Bulanan Pasukan Rusia, Terus Bombardir Pertahanan Ukraina
| |
| меню : 856 |  3
|  Кузовной ремонт, сварочные работы, рихтовка, покрасочные работы, замена стекла, замена порогов, подробнее ЛС или WhatsApp telegram 89833432025 |
|
меню : 855 |  1
| |
| меню : 853 |
ПРОТИВОСТОЯНИЕ 1985
| |
| меню : 836 | Молодая пара снимет квартиру в городе Колпашево. Предложения рассмотрю в лс, или WatsApp +79962053101
| |
| меню : 834 |  1
|  Тренируем ум: сможете ли найти хотя бы 3 отличия на двух картинках? |
|
меню : 830 |  1
|  Сыр-Капчо - деликатес на заказ
+7 983 239-71-94 |
| меню : 829 |  1
|  Чо-капчо 8-983-239-71-94 ИП Муратова О.В.
Всем продуктивной недели! Снова в наличии на доставку завтра :
ароматные копчёные курочки
любимые рёбрышки
мясной орех (шея)
Сыр-Капчо - деликатес на заказ
Пишите! Успевайте насладиться копчёными на фруктах вкусняшками
Доставка по городу бесплатная от любой суммы! |
| меню : 815 |
БАЯНИСТ в ГРУЗИИ | Реакция ПРОХОЖИХ на БАЯН и ГИТАРУ ft. Гитара с нуля
| |
| меню : 814 |
Жириновский предсказал будущее Европы ...
| |
|
меню : 813 |
Образцовый хореографический коллектив "Радуга" привёз с "Кубка Сибири" 7 дипломо
| |
| меню : 812 |
Начались работы по обустройству ледовой переправы через Обь в районе Колпашева
| |
| меню : 809 |
В Колпашеве установили вертолёт на памятном мемориале на ул. Горького в районе пристанской площади
| |
| меню : 808 |
Покровские ворота 2 серия (FullHD, комедия, реж. Михаил Козаков, 1982 г.)
| |
|
меню : 807 |
Покровские ворота 1 серия (FullHD, комедия, реж. Михаил Козаков, 1982 г.)
| |
| меню : 806 |  1
|  1948 год, Москва, Елисеевский гастроном, 3 года как война закончилась.
Чёрная и красная икра вразвес, "плохой Сталин." |
| меню : 805 | Олег Тиньков решил отказаться от российского гражданства
МОСКВА, 31 октября. /ТАСС/. Предприниматель и бывший владелец «Тинькофф банка» Олег Тиньков решил отказаться от российского гражданства. Об этом он сообщил в понедельник на своей странице в Instagram (запрещен в РФ; принадлежит корпорации Meta, которая признана в РФ экстремистской).
| |
| меню : 802 |  1
|  Международная команда исследователей во главе с археологами Тель-Авивского университета и Еврейского университета в Иерусалиме изучила артефакты из 17 регионов Израиля. Все они были связаны с городами, разрушенными огнем. Это позволило ученым использовать память магнитного поля и решить несколько давних загадок. Они реконструировали конфликты с 12 по 6 века до н. э., связанные с падением царств Израиля и Иудеи. Все они, как оказалось, были описаны в Библии.
Святая земля часто подвергалась нападениям завоевателей. С войной на нее приходили Египет, Арамеи, Ассирия, Вавилон и другие империи против царств Израилы и Иудеи. Поэтому сегодня Израиль богат на древние руины, но кто, что и когда разрушил, по-прежнему заставляет археологов спорить и ломать голову. |
|
меню : 801 |
C# OpenGL Setup Perspective ViewPort
| |
| меню : 800 |
c# Cad program openGL для кадастровых инженеров created by me cad program.
| |
| меню : 798 |
c# ComboBox
| |
| меню : 797 |
БАЯНИСТ В ЧАТРУЛЕТКЕ/ПИКАП КРАСИВЫХ ДЕВУШЕК/ ЛУЧШИЕ РЕАКЦИИ
| |
|
меню : 795 |
Урок 1. Курс по ООП PHP. Класс и объект
| |
| меню : 794 |
AMAZING HANDYMAN CRAFTS AND TIPS
| |
| | |
меню : 790 |
Trigonometry Concepts - Don't Memorize! Visualize!
| |
| меню : 789 |
Колпашевцы приняли участие в выборах в минувшие выходные
| |
| меню : 787 |
БАЯННАЯ ЧАТРУЛЕТКА/ПИКАП ДЕВУШЕК/ЛУЧШИЕ РЕАКЦИИ/ХИЖИНА МУЗЫКАНТА
| |
| меню : 786 |  1
|  Приму Шишку дорого В любое время 24/7 Цена от 1450 и выше
Любые объемы
Заберём с места Звоните тел.+7-962-779-58-08 |
|
меню : 780 |
Попробуй НЕ ЗАПЛАКАТЬ! 25 Трогательных Моментов, Снятых на Камеру
| |
| меню : 771 |  1
|  Продам садовые яблоки 1 литр 100 рублей. 25.08.22 года
Тел. 89539112923 |
| меню : 770 |
Камеди Клаб. Демис Карибидис, Марина Кравец, Яна Кошкина «Я не такая»
| |
| меню : 768 |  1
|  Продам помидоры. Тел: 89539112923 город Колпашево |
|
меню : 753 | ФСБ раскрыла убийство Дугиной
МОСКВА, 22 августа. /ТАСС/. Убийство журналистки Дарьи Дугиной раскрыто, его подготовили украинские спецслужбы, исполнительница — гражданка Украины Наталья Вовк, скрывшаяся после преступления в Эстонии. Об этом ТАСС сообщили в понедельник в Центре общественных связей (ЦОС) ФСБ России. | |
| меню : 727 |
БАЯНИСТ В ЧАТ РУЛЕТКЕ #2 | Встретил NILETTO | Реакция людей на ПЕСНИ НА БАЯНЕ
| |
| меню : 725 |  1
| |
| меню : 719 |  1
|  Куплю морозильную камеру 8- 953 - 911 - 29 - 23. |
|
меню : 717 |
НОВЫЕ реакции в ЧАТ-РУЛЕТКЕ/реакции ДЕВУШЕК/уломал красотку
| |
| меню : 713 |
10 ФАНАТОВ КОТОРЫЕ ПЕРЕПЕЛИ СВОИХ КУМИРОВ!!! ТОП 10
| |
| меню : 709 |  1
|  Куплю советские радиодетали, платы, приборы, блоки радиостанций, радиотехнический лом 89138835150 |
| |
меню : 699 |  1
| |
| меню : 698 |  1
|  Продам кирпич красный полнотелый б/у.
Цена 10 рублей за штуку. Объём большой.
Возможна доставка по городу и району. По всем вопросам обращайтесь по телефону 8-923-421-74-76 или в личные сообщения. |
| меню : 697 |  1
| |
| меню : 694 |  1
| |
|
меню : 693 |  1
| |
| меню : 692 | https://zen.yandex.ru/video/watch/629d06f425adf41808a505aa | |
| меню : 691 |  1
| |
| меню : 690 |  1
|  Продам пиломатериал хвойных пород все в наличии
Отдам опилок с места
Всегда в продаже Горбыль
89528864131 |
|
меню : 689 |
Колпашево - Тогур. Осень 2018
| |
| меню : 688 |  1
|  ПРОДАМ длинна 2,05 ширина 1,60. 89539255172 |
| | меню : 686 |  3
|  Приехать и выбрать можно по адресу: город Колпашево пер.Коммунальный 5. Предварительный звонок по телефону 8-953-911-29-23 Наталья. Возможна ДОСТАВКА. |
|
| меню : 682 | Продам рассаду цветов : Лобелия( красная, белая, синяя, лиловая, синяя с белой серединкой, розовая). Петуния ( ампельная, махровая, крупноцветковая).Тел. 89539112923
Колпашево | |
| меню : 680 |  6
|  Продам рассаду цветов : Лобелия( красная, белая, синяя, лиловая, синяя с белой серединкой, розовая). Петуния ( ампельная, махровая, крупноцветковая).Тел. 89539112923 |
| меню : 679 | Ремонт, продажа сотовых телефонов и планшетов; замена стекол iphone и прочих телефонов; удаление гугл аккаунтов, mi-cloud, графических ключей и т.д.
"Купеческий" второй этаж 89528047975 Обращаться ТОЛЬКО В РАБОЧЕЕ ВРЕМЯ:
ПН - ПТ с 10:00 до 17:00
СБ и ВС - Выходной
Скупка телефонов на запчасти.
Промывка инжектора, автодиагностика, прошивка эбу 89521547256
Продам ЭБУ для ваз . Январь 5,1 и Bosh 1.5.4 89521547256
Продам Samsung g532f 2500р | |
|
меню : 678 |  1
| |
| меню : 677 |  1
| |
| меню : 676 |  1
| |
| меню : 675 |  1
| |
|
меню : 673 |  1
| |
| | меню : 664 |  1
| |
| меню : 663 |  1
|  Продам пиломатериал хвойных пород.
Так же в наличии осиновый пиломатериал.
Продам Горбыль пиленный.
Отдам опилок с места.
89528864131 |
|
меню : 662 |  1
|  Рубим срубы на заказ. 89528051212 |
| меню : 661 |  1
|  Отдам опилок с места.
Продам горбыль пиленный.
89528864131 |
| меню : 660 |  1
| |
| меню : 659 |  1
| |
|
меню : 658 |  1
| |
| меню : 657 |  1
|  Город Колпашево, рубим срубы на заказ. 89138640125 |
| меню : 656 |  1
| |
| меню : 655 |  1
|  Город Колпашево |
|
меню : 654 |  2
|  Колпашево Продам холодильник БЕКО в хорошем техническом состояние высота 1.60м 8-900-921-32-30 |
| меню : 653 |  1
| |
| меню : 652 |  1
|  Город Колпашево |
| меню : 651 |  1
|  Город Колпашево.
ОТДАМ за килограмм фруктов кожаное пальто 48-50 р. в связи с переездом
Тел.89138265025 |
|
меню : 650 |  1
| |
| меню : 649 | В России создан перечень из 60 компаний-претендентов на национализацию
В правительство и Генпрокуратуру направлен список иностранных компаний, которые в перспективе могут быть национализированы. Перечень, подготовленный организацией «Общественная потребительская инициатива», имеется в распоряжении «Известий». В него вошло порядка 60 компаний, объявивших о прекращении работы в России без предоставления гарантий потребителям. В их числе Volkswagen, Apple, IKEA, Microsoft, IBM, Shell, McDonald’s, Porsche, Toyota, H&M и др. | |
| меню : 648 | WSJ: лидеры ОАЭ и Саудовской Аравии отказались общаться с Байденом об Украине
НЬЮ-ЙОРК, 9 марта. /ТАСС/. Лидеры двух ближневосточных государств отказались от телефонных переговоров с президентом США Джо Байденом, который хотел обсудить ситуацию вокруг Украины и возможность наращивания нефтедобычи для снижения мировых цен на углеводороды. Об этом сообщила во вторник газета The Wall Street Journal. | |
| меню : 647 | Путин: устанавливаются новые выплаты на детей 8−16 лет для семей с невысокими доходами
МОСКВА, 8 марта. /ТАСС/. Президент России Владимир Путин объявил, что все семьи с невысокими доходами будут получать выплаты на детей 8−16 лет. | |
|
меню : 646 | Венгрия разрешила размещать войска НАТО на своей территории
Премьер-министр Венгрии Виктор Орбан подписал указ, разрешающий размещение войск НАТО на западе страны и поставки оружия через свою территорию в другие государства-члены НАТО. | |
| меню : 645 | В ЦБ заявили, что карты Visa и Mastercard российских банков продолжат работать в РФ
МОСКВА, 6 марта. /ТАСС/. Карты международных платежных систем Visa и Mastercard всех российских банков продолжат работать в РФ, средства клиентов на счетах, привязанных к таким картам, полностью сохранены и доступны. Об этом сообщил Банк России. | |
| меню : 644 | Российские войска заняли украинскую военную базу в Херсонской области
МОСКВА, 5 мар — РИА Новости. Вооруженные силы России взяли под контроль украинскую военную базу около села Раденьск Херсонской области, сообщили журналистам в Минобороны. | |
| меню : 643 | На Украине заочно арестовали Никиту Михалкова
МОСКВА, 4 мар — РИА Новости. Суд на Украине заочно арестовал российского режиссера Никиту Михалкова по подозрению в «посягательстве на территориальную целостность» страны, заявила генпрокурор Украины Ирина Венедиктова. | |
|
меню : 642 | Кадыров заявил, что происходящее на Украине «перешло все рамки морали»
За девять дней спецоперации на Украине стало очевидно, что события в стране «перешли все рамки морали», заявил в Telegram глава Чечни Рамзан Кадыров. Он вновь призвал Россию не идти ни на какие уступки на переговорах. | |
| меню : 641 | 45 минут назад, источник: Lenta.Ru
В Минобороны сообщили о налетах иностранных наемников на российские колонны | |
| меню : 640 | Маргарита Симоньян выходит замуж после 10 лет отношений | |
| меню : 639 | Необоснованное поднятие цен прировнять к мародёрству. | |
|
меню : 638 | Евросоюз - это объединение стран которые воевали против советского союза.
Немцы мечтают о восстановление или создании нового рейха.
А, также о реванше. Поэтому там всегда была и будет русофобия и ненависть к русским.
По этому и будут поставки оружия на Украину. | |
| меню : 636 |
Намедни - 94. Вывод войск из Германии
| |
| меню : 635 | Вот почему, Россия сейчас воюет. | |
| меню : 634 |
Пьяный Ельцин в Германии
| |
|
меню : 633 |
Видео уничтожения российских вертолетов, совершивших атаку на аэродром в Киеве
| |
| меню : 632 |
Вертолётный десант в Киевской области.
| |
| меню : 631 |  1
|  Кадыров заявил о готовности отправить чеченцев в самые горячие точки Украины |
| |
меню : 629 | СМИ сообщают о взрывах в Киеве, Харькове, Одессе и других городах Украины | |
| меню : 628 | Военная операция на Украине. Главное
Путин выступил с обращением и объявил о начале военной операции. Главное о ситуации вокруг Украины — в материале | |
| меню : 624 |
Колпашево
| |
| меню : 623 |  1
|  Путин сообщил Макрону и Шольцу, что скоро подпишет указ о признании ДНР и ЛНР
Как отметили в Кремле, президент Франции и канцлер ФРГ «выразили разочарование таким развитием» событий, однако обозначили готовность к продолжению контактов. |
|
меню : 622 |
Перелет из Азии в Америку / Русский Нью-Йорк / Особняк Усманова в США
| |
| | | меню : 619 |
Как разместить ссылку на видео на нашем сайте setom.ru
| |
|
меню : 618 |
C# PropertyGrid
| |
| меню : 616 |
Камеди Клаб «Табличка» Харламов, Батрутдинов, Кравец, Иванов, Аверин, Скороход, Смирнов
| |
| меню : 614 |
VID 20220118 184226
| |
| меню : 613 |
Пятизвездочный отель в Доминикане Lopesan Costa Bávaro Resort и неуравновешенный охранник
| |
|
меню : 612 |
Колпашево - Тогур. Осень 2018
| |
| меню : 609 |
КУЧА КЛОНОВ ПОЗЗИ НА КАРТЕ ДЛЯ ПОЗЗИ ОББИ! | Roblox
| |
| меню : 608 |
Шерлок Холмс и доктор Ватсон | 5 серия | Охота на тигра
| |
| меню : 606 |
Шерлок Холмс и доктор Ватсон | 4 серия | Смертельная схватка
| |
|
меню : 605 | Продам коньки размер 36-37, цена 500 рублей. Тел. 89539112923 | |
| меню : 601 |
Колпашево - Селекционная
| |
| | меню : 597 |
Колпашево - Тогур. Осень 2018
| |
|
меню : 596 |
Колпашево Геолог
| |
| меню : 593 |
Камеди Клаб «Жена на совещании» Карибидис, Кравец, Батрутдинов, Аверин, Иванов, Бутусов
| |
| | меню : 591 |
Party Dance Music 2021 (Bass Boosted) - Best Dance (Shuffle Dance) HQ VideoClip
| |
|
меню : 590 | Колпашево Продам свежих карасей. Цена 60-70-80 руб. 89627830451 | |
| меню : 589 |  1
|  Продам USB микрофон Blue Yety. Цена 7.000 В подарок отдам пантограф. |
| меню : 588 |  1
|  Колпашево, продам уголок школьника. 3500р. Тел 89138023736 |
| меню : 587 |  1
|  Томская область, город Колпашево |
|
меню : 585 |  5
|  ЗИМНЕЕ УТРО
Мороз и солнце; день чудесный!
Еще ты дремлешь, друг прелестный —
Пора, красавица, проснись:
Открой сомкнуты негой взоры
Навстречу северной Авроры,
Звездою севера явись! |
| меню : 584 |
Эти Животные ЗВАЛИ НА ПОМОЩЬ!
| |
| меню : 582 |
Попробуй НЕ ЗАПЛАКАТЬ! Топ 10 Моментов Встречи Животных с Хозяевами После Долгой Разлуки
| |
| меню : 581 |
Санаторий Чажемто приглашает!
| |
|
меню : 580 |
Интервью. Вячеслав Фишер, генеральный директор санатория "Чажемто"
| |
| меню : 579 |
ТОП-5 ОЧЕНЬ УГАРНЫХ КОМЕДИЙ!#3 (+18 )
| |
| меню : 577 |
War thunder Большой бада - бууум !!!
| |
| меню : 576 |  1
| |
|
меню : 571 |
Камеди Клаб «Прослушка» Тимур Батрутдинов, Андрей Скороход
| |
| меню : 570 | https://vk.com/feed | |
| меню : 568 |
Рейс Майами - Чикаго / Помогаю мексиканскому коллеге / Попал под низкий мост
| |
| меню : 567 |
Камеди Клаб «День отца» Павел Воля
| |
|
меню : 566 |
Гарик Харламов разговаривает с мошенниками по телефону (ч. 2)
| |
| меню : 564 |
Камеди Клаб Джиган, Blago White, Понасенков, Харламов, Воля
| |
| меню : 563 |
War thunder Большой бада - бууум !!!
| |
| меню : 560 |
CAR MUSIC MIX 2021
| |
|
меню : 559 |
Junona Boys - Relax (Премьера 2020)
| |
| меню : 558 |
Смешные испуги людей,юмор,пранки,розыгрыши!!! #20 0620 SCARE PRANK COMPILATION
| |
| меню : 557 |
Михайло Ломоносов. От недр своих. (Фильм 1, серия 1. Биографический)
| |
| меню : 556 |
Камеди Клаб «Потеряла телефон» Марина Кравец, Тимур Батрутдинов, Гарик Харламов
| |
|
меню : 555 |
ЛУЧШИЕ РЕАКЦИИ ВОКАЛИСТОВ И БЛОГЕРОВ НА ДИМАША
| |
| меню : 554 | Продам Кур несушек 300 рублей за штуку, возраст 3-4 месяца.
WhatsApp 89131164579 | |
| меню : 553 |
ОНИ ВПЕРВЫЕ СЛЫШАТ ДИМАША | ПЕРВАЯ РЕАКЦИЯ ВОКАЛИСТОВ И БЛОГЕРОВ НА ДИМАША
| |
| меню : 552 | США утверждают, что Россия провела опасное испытание противоспутникового оружия
Госдепартамент США выпустил заявление по поводу якобы состоявшегося на выходных испытания Россией противоспутникового оружия. При этом утверждается, что образовавшиеся в ходе испытания обломки могли угрожать Международной космической станции. «Ъ» направил запросы в «Роскосмос» и Министерство обороны. | |
|
меню : 550 |
▶️ Инспектор ДПС, который СМОГ!
| |
| меню : 549 |
War thunder Большой бада - бууум !!!
| |
| меню : 548 |
war thunder
| |
| меню : 547 |
war thunder Антон
| |
|
меню : 545 |
Пушка 2А42 Уничтожает БРДМ-2 | 30mm Autocannon crushes armored car | Крупнокалиберный переполох
| |
| меню : 544 |
БМП-1 Против БМП-2 / Смертельная гонка
| |
| меню : 543 |
Tell Him - Emi Pellegrino & Kristy Bissell (Celine Dion & Barbra Streisand COVER)
| |
| меню : 542 |
Камеди Клаб Воля Харламов Милохин Бабич Покров
| |
|
меню : 541 |
Камеди Клаб Гаврилина, Хомяк, Dabro, Харламов, Воля
| |
| меню : 540 |  4
| |
| меню : 539 |
Поздравления с новым годом Лёши !
добавить текст | |
| меню : 538 |  1
|  добавить текст |
|
| меню : 504 |
Танк Tiger H1 обстрел из Marder 88 Pokémon с поджегом и жидким не отрывом башни
| |
| меню : 503 |  3
| |
| |
меню : 497 |
ПЕРВАЯ РЕАКЦИЯ УЧИТЕЛЕЙ ПО ВОКАЛУ НА ДИМАША | ЧАСТЬ 4 | FIRST REACTION VOCAL COACHES TO DIMASH
| |
| меню : 496 |
Dimash - AVE MARIA | Новая Волна 2021
| |
| меню : 447 | Премьер-министр России Михаил Мишустин поддержал идею о введении нерабочих дней по всей стране с 30 октября по 7 ноября включительно. Об этом во вторник, 19 октября, сообщает ТАСС. | |
| меню : 446 |  5
| |
|
меню : 442 |
C# OpenGL
| |
| меню : 439 |
Попробуй НЕ ЗАПЛАКАТЬ! Топ 30 Самых Трогательных и Добрых Моментов, Снятых На Камеру #6
| |
| меню : 438 |
КВН Русская дорога - Сборник миниатюр
| |
| меню : 437 |
Димаш - STRANGER / Новая Волна 2021
| |
|
меню : 436 |
За СЕКУНДУ ДО... Самые Жуткие КРУШЕНИЯ Кораблей, Снятые на Камеру
| |
| меню : 435 |  1
| |
| меню : 434 |  1
|  Продам садовые яблоки 5 литров 450 рублей.
Тел. 89539112923 |
| меню : 433 | https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Graphics/DxtUtil.cs | |
|
меню : 432 |  2
| |
| меню : 431 |
ПРИКОЛЫ В ТИК ТОК ДЛЯ ВЗРОСЛЫХ | ЛУЧШИЕ ПРИКОЛЫ ИЗ ТИК ТОК 2021 | ТРЕНДЫ И ЗНАМЕНИТОСТИ #1
| |
| меню : 430 |  1
| |
| меню : 429 |  3
|  Продам гараж с документами. Район пристани. Тел. 89539112923 Колпашево |
|
меню : 428 |  1
|  Предлагаем Вашему вниманию квартиры посуточно в Колпашево.
Тел: 8-913-107-82-12
Ссылка на квартиры в моем профиле! |
| меню : 427 |  11
|  Продам рассаду цветов : Лобелия( красная, белая, синяя, лиловая, синяя с белой серединкой, розовая). Петуния ( ампельная, махровая, крупноцветковая).Тел. 89539112923 Колпашево |
| меню : 426 | Продам Прогресс 2М с документами Все вопросы по телефону 55000 тел 89539264748 Колпашево | |
| меню : 425 |  1
| |
|
меню : 424 |  1
| |
| меню : 423 |  1
|  Продам акустику 89528911388 Колпашево |
| меню : 422 |
Лесной календарь
| |
| меню : 421 |
Камеди Клаб «Отгоните машину» Харламов Карибидис Батрутдинов Иванов Гореликов Половинкин Бутусов
| |
|
меню : 420 |  1
| |
| меню : 415 | .jpg) 1
|  Предлагаем Вашему вниманию квартиры посуточно в Колпашево.
Тел: 8-913-107-82-12 |
| меню : 414 | Продам участок в центре Тогура Октябрьская 23 все вопросы по тел 89528861443, 89539265022 (я посредник только звонить) | |
| меню : 409 |
Замерзла гидравлика / Проблемы с электрикой / Клиент не отдает деньги
| |
|
меню : 408 |  1
|  Требуются рамщик, кромщик, разнорабочий. По всем вопросам звоните 89528864131 |
| меню : 407 |  1
|  Нужна модель на маникюр с покрытием гель-лак в это вс 17:30.
Стоимость - 300 рублей.
Запись в лс или WA 89539279015. |
| меню : 406 |  2
|  Пиломатериал в наличии и под заказ. Срезка пиленная 3500р. 89528051212 |
| меню : 402 |
Камеди Клаб Харламов Батрутдинов Скороход «Чинововирус»
| |
|
меню : 401 |
Колпашевский авиаотряд
| |
| меню : 400 | Продаю 2-х комнатную квартиру на втором этаже двухэтажного деревянного дома в Тогуре. Рядом школа, магазин, остановка. Имеется небольшой огород. В квартире установлен слив, пластиковые окна. Квартира обшита сайдингом и утеплена. Отопление централизованное. Соседи тихие, дружные. Один собственник. Цена 700000 руб. Тел. 89069593006 | |
| меню : 394 |  4
|  ❗????Продам Квартиру в доме (Панова 56а) возможно под материнский капитал с доплатой, две комнаты, кв тёплая, общая площадь 31,1. Имеется гараж и баня.????❗
Обращаться по телефону: 89627879625 |
| меню : 393 |  1
|  Колпашево Услуги грузоперевозки (газель) 89131094929 |
|
меню : 390 | Колпашево Куплю авто битое неисправное без доков и тд 89539196454 | |
| меню : 388 |  1
|  Поступление автошин Магазин Автолидер 1км Автодороги Колпашево Тогур 89521785137 |
| меню : 386 |
Камеди Клаб Новый сезон Гарик Харламов Павел Воля Савко Ольга Бузова
| |
| меню : 382 |
Камеди Клаб Новый сезон Харламов Воля Клава Кока NILETTO DAVA
| |
|
меню : 381 |
Камеди Клаб Премьера Павел Воля «Мы неубиваемые»
| |
| меню : 380 |  7
| |
| меню : 378 |
До слёз! Трогательные Поступки Месси, Роналду и Неймара!
| |
| меню : 377 |  1
| |
|
меню : 376 |  1
|  Благодapя пpинятым пoпрaвкам в нынешнее зaкoнодaтельcтво
Kаждый coвершенoлетний имеет пpaвo запoлучить финанcовые кoмпенсации зa 2007-2020 год
B связи с принятием пocтановления o поддержке наcеления в связи c нaциoнальным пpoектом o рaзвитии импортoзaмещения
Любой мoжет пpовеpить cебя заполнив пpостую фopмy на реcyрсе |
| меню : 373 |  1
| |
| | меню : 370 |  3
| |
|
меню : 368 | В Томской области пройдет экомарафон «Марш парков — 2021». Он посвящен поддержке особо охраняемых природных территорий, говорится на сайте «Облкомприроды».
Марафон приурочен к международной акции «Марш парков — 2021». Она проводится ежегодно, объединяя всех неравнодушных людей вокруг идеи поддержки заповедников, национальных парков, заказников и памятников природы.
«В томском марафоне могут поучаствовать лица или команды от трех человек — детские, взрослые, смешанные. Возрастных ограничений нет. От одной организации может участвовать несколько команд», — говорится в сообщении.
| |
| меню : 367 |
Я РЖАЛ ДО СЛЕЗ ???? НОВЫЕ ПРИКОЛЫ / Смешные Видео / Смешные испуги людей и животных . подборка 2021
| |
| меню : 366 | В Томской области пройдет экомарафон «Марш парков — 2021». Он посвящен поддержке особо охраняемых природных территорий, говорится на сайте «Облкомприроды».
Марафон приурочен к международной акции «Марш парков — 2021». Она проводится ежегодно, объединяя всех неравнодушных людей вокруг идеи поддержки заповедников, национальных парков, заказников и памятников природы.
«В томском марафоне могут поучаствовать лица или команды от трех человек — детские, взрослые, смешанные. Возрастных ограничений нет. От одной организации может участвовать несколько команд», — говорится в сообщении.
Участники экомарафона смогут запустить акции: экологических десанты, уборка, благоустройство, развешивание гнездовий. Помимо практической помощи, в списке активностей — распространение листовок, буклетов, создание тематических интернет-страниц, открытые уроки, семинары и так далее. Хэштег экомарафона: #марафонМПТО2021.
₽ | |
| меню : 364 |  5
| |
|
меню : 360 |
КВН ТОП лучших выступлений в Премьер лиге участников Высшей лиги! Часть 2
| |
| меню : 359 |
КВН ТОП лучших выступлений в Премьер лиге участников Высшей лиги! Часть 1
| |
| меню : 322 |
19 МИНУТ ОТБОРНЫХ ПРИКОЛОВ ДЛЯ ВЗРОСЛЫХ МУЖИКОВ. СМЕШНЫЕ ВИДЕО 2021 МАРТ Приколы от КОТА
| |
| меню : 321 |
Rick Astley - Never Gonna Give You Up (Video)
| |
|
меню : 318 |
Место встречи изменить нельзя (1979) криминальный детектив
| |
| меню : 316 |
ПОЛОСАТЫЙ РЕЙС (советский фильм комедия 1961 год)
| |
| меню : 315 |
ШОФЕР ПОНЕВОЛЕ (советский фильм комедия 1958 год)
| |
| меню : 312 |
Максим Галкин – Большой сольный концерт - Москва - Государственный Кремлевский Дворец
| |
|
меню : 308 |
Посев томатов на рассаду. Сроки и способы посева
| |
| меню : 305 | Педагог по вокалу из Томска, 25-летний Сергей Павловский, занял первое место в очередном этапе шоу «Ну-ка, все вместе!», которое выходит на канале «Россия-1».
Сергей исполнил композицию «Верни мне музыку» Муслима Магомаева и набрал 93 голоса из 100 возможных. Выступление томича оценило жюри, состоящее из джазовых див, оперных певцов, исполнителей народных песен, именитых спортсменов, преподавателей вокала, топовых TikTok-блогеров и других знаменитостей. | |
| меню : 303 |
Hits Radio 1 Top Songs • Live Radio Pop Music 2020' Best English Songs 2020 - New Popular So
| |
| меню : 302 |
Концерт Максима Галкина. Выпуск от 20.01.2017
| |
|
меню : 301 |
ЭКСТРЕМАЛЬНОЕ ОГРАБЛЕНИЕ МИЛЛИОНЕРА! ЧЕЛЛЕНДЖ
| |
| меню : 299 |
Hits Radio 1 • Live Radio Pop Music 2020' Best English Songs Of All Time - New Popular Songs
| |
| меню : 274 |
Турецкая езда без правил / По Стамбулу без авто
| |
| меню : 268 |
Тогур
| |
|
меню : 262 |
20 Героических Поступков, Которые Сожмут Ваше Сердце!
https://youtu.be/NHaGRnfaG6k | |
| меню : 261 |
Несколько дней назад была закрыта ледовая переправа в Колпашево
https://youtu.be/w5kV7GOslyM | |
| меню : 260 |
Dance Radio Live• Dance Music 2020' Best English Songs 2020' Top Hits 2020'
https://www.youtube.com/watch?v=YSBO7Zl8mU4 | |
| меню : 259 |
Смешные испуги людей,юмор,пранки,розыгрыши!!! #09 0320 SCARE PRANK COMPILATION
https://youtu.be/VBMLa4LawtA | |
|
меню : 201 | Сдам или Продам двухкомнатную благоустроенную квартиру в центре тогура вопросы по телефону 89521597677 | |
| меню : 184 |
Открытие фонтана в городе Колпашево
https://www.youtube.com/watch?v=x4zPah__zxM | |
| меню : 182 | Японцы приблизились к созданию батареек на искусственных алмазах, которые смогут работать сотни лет 01.03.2021 [14:57], Алексей Разин Японские учёные близки к созданию источников питания, способных работать по сто лет без необходимости замены | |
| меню : 174 | Hallo all.write Get the number of pixels the content of "myDIV" is scrolled: ghcvjgcgcjhg | |
|
меню : 173 |
Приколисты. ТОП-10. Десятка Лидеров - миллионы просмотров!
https://www.youtube.com/watch?v=7gUZF6RmXeg | |
| меню : 171 |
Медведь в городе (ВИДЕО) г. Колпашево
https://www.youtube.com/watch?v=XKv3Udd69T8 | |
| меню : 169 |
В Томской области медвежонок качался на качелях
https://www.youtube.com/watch?v=OeiI-msGLdI | |
| меню : 168 |
Колпашево - Тогур. Осень 2018
https://www.youtube.com/watch?v=78OIOerlwy8 | |
|
меню : 167 |
Агрессивная собака возле входа в Колпашевское отделение сбербанка
https://www.youtube.com/watch?v=Ae94NO4wpBQ | |
|