# Normals transformation problem

I am working on my first Direct3D game and even after many days of searching Internet and experimenting I can't figure out how to work with normals properly. As a result, the lights are not working correctly. The scene is shown on the below picture.

The camera is always pointed into center of the scene (0,0,0) and rotates around it. The scene itself is static, only the vehicles can move forward or backward. The light source is a static point light. So I would presume the diffuse lighting level of the objects must be static during camera rotation. But it isn't. When I rotate the camera, the lighting levels of particular parts of objects are changing (even if I disable the specular part of light). Also the diffuse light on the objects is not correct with respect to the light position. So I would say I am doing something completely wrong with the normals. I am trying to figure it out for 1 week but with no success. So I am kindly asking you for any help which can push me forward.

I have found (e.g. here), that I must multiply my normal vectors with transposed inverse matrix of the original world matrix applied to my vertices, because my models are scaled. But this is not helping. Below are relevant parts of my code.

Render scene method:

struct CBUFFER {
DirectX::XMMATRIX world;
DirectX::XMMATRIX rotation;
DirectX::XMMATRIX invTrWorld;
DirectX::XMVECTOR cameraPosition;
DirectX::XMVECTOR lightVector;
DirectX::XMVECTOR diffuseColor;
DirectX::XMVECTOR ambientColor;
float specularPower;
DirectX::XMVECTOR specularColor;
};

void Game::Render() {
CBUFFER cBuffer;
XMMATRIX matView, matPerspective;
XMVECTOR camPosition = XMVectorSet(0.0f, 4.0f, -10.0f, 0.0f);
camPosition = XMVector4Transform(camPosition, _rotation); // _rotation - defines the angle of the camera

//Set the View matrix
matView = XMMatrixLookAtLH(
camPosition,                          // the camera position (rotating around the center of the board)
XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f),  // the look-at position
XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f)   // the up direction
);
cBuffer.cameraPosition = camPosition;

// create a projection matrix
matPerspective = XMMatrixPerspectiveFovLH((FLOAT)XMConvertToRadians(45), (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, 1.0f, 100.0f);

cBuffer.lightVector = XMVectorSet(-10.0f, 10.0f, 10.0f, 1.0f);
cBuffer.diffuseColor = XMVectorSet(1.0f, 1.0f, 1.0f, 1.0f);
cBuffer.ambientColor = XMVectorSet(_ambientColorIntensity, _ambientColorIntensity, _ambientColorIntensity, 1.0f);
cBuffer.specularColor = XMVectorSet(1.0f, 1.0f, 1.0f, 1.0f);

// clear the back buffer to a deep blue
FLOAT bgColor[4] = { 0.0f, 0.2f, 0.4f, 1.0f };
_d3d->GetDeviceContext()->ClearRenderTargetView(_d3d->GetBackBuffer(), bgColor);

// clear the depth buffer
_d3d->GetDeviceContext()->ClearDepthStencilView(_d3d->GetZBuffer(), D3D11_CLEAR_DEPTH, 1.0f, 0);

// select which vertex buffer to display
UINT stride = sizeof(VERTEX);
UINT offset = 0;
_d3d->GetDeviceContext()->IASetIndexBuffer(_d3d->GetIBuffer(), DXGI_FORMAT_R32_UINT, 0);

// select which primtive type we are using
_d3d->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
_d3d->GetDeviceContext()->IASetInputLayout(_d3d->GetLayout());

// select Rasterizer and Sampler configuration

// Draw model instances - Non-vehicle objects
for (auto it = _minstances.begin(); it != _minstances.end(); it++) {
ModelInstance mi = it->second;

// Store instance transformation into constant buffer
XMMATRIX worldMatrix = mi.GetTransformation() * _worldOffset;
worldMatrix *= matView * matPerspective;
XMMATRIX trInvWorld = XMMatrixInverse(nullptr, XMMatrixTranspose(worldMatrix));
cBuffer.world = worldMatrix;
cBuffer.invTrWorld = invTrWorld;
// TODO: obtain specularPower from model
cBuffer.specularPower = 1.0f;
// Send constant buffer
_d3d->GetDeviceContext()->UpdateSubresource(_d3d->GetCBuffer(), 0, 0, &cBuffer, 0, 0);

for (auto i : mi.GetModel().GetMeshEntries()) {
// select texture
_d3d->GetDeviceContext()->DrawIndexed(i._numIndices, i._baseIndex, i._baseVertex);
}
}

// Draw vehicles
for (auto it = _vehicles.begin(); it != _vehicles.end(); it++) {
Vehicle mi = it->second;
if (mi.IsHidden()) {
continue;
}
// Store vehicle transformation into constant buffer
XMMATRIX worldMatrix = mi.GetTransformation() * _worldOffset;
worldMatrix *= matView * matPerspective;
XMMATRIX trInvWorld = XMMatrixInverse(nullptr, XMMatrixTranspose(worldMatrix));
cBuffer.world = worldMatrix;
cBuffer.invTrWorld = invTrWorld;
// Store vehicle color into constant buffer
XMVECTOR vehicleColor = mi.GetColor();
cBuffer.diffuseColor = vehicleColor;
cBuffer.specularColor = vehicleColor;
float glowIntensity = _ambientColorIntensity + mi.GetGlowLevel();
cBuffer.ambientColor = XMVectorSet(vehicleColor.m128_f32[0] * glowIntensity,
vehicleColor.m128_f32[1] * glowIntensity,
vehicleColor.m128_f32[2] * glowIntensity,
vehicleColor.m128_f32[3] * glowIntensity);
// TODO: obtain specularPower from model
cBuffer.specularPower = 32.0f;
// Send constant buffer
_d3d->GetDeviceContext()->UpdateSubresource(_d3d->GetCBuffer(), 0, 0, &cBuffer, 0, 0);

for (auto i : mi.GetModel().GetMeshEntries()) {
// select texture
_d3d->GetDeviceContext()->DrawIndexed(i._numIndices, i._baseIndex, i._baseVertex);
}
}

// print FPS info
_d2d->PrintInfo();

// switch the back buffer and the front buffer
_d3d->GetSwapChain()->Present(0, 0);
}


HLSL code:

cbuffer ConstantBuffer {
matrix world;
matrix rotation;
matrix trinvworld;
float4 camposition; // position of the camera
float4 lightpos; // the diffuse light's vector
float4 diffusecol; // the diffuse light's color
float4 ambientcol; // the ambient light's color
float specularpow; // the specular light's power
float4 specularcol; // the specular light's color
}

Texture2D Texture;
SamplerState ss;

struct VOut {
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 position : SV_POSITION;
};

VOut VShader(float4 position : POSITION, float3 normal : NORMAL, float2 texcoord : TEXCOORD) {
VOut output;

// VERTEX POSITION TRANSFORMATION
// Calculate the position of the vertex in the world
float4 worldposition4 = mul(world, position);
//  float3 worldposition = mul((float3x3)world, position.xyz);
output.position = worldposition4

// AMBIENT LIGHT & NORMAL TRANSFORMATION
float4 color = ambientcol;
float3 norm = normalize(mul((float3x3)trinvworld, normal));
//float3 norm = normalize(mul((float3x3)world, normal));

// DIFFUSE LIGHT
//  float3 lightpos3 = lightpos.xyz;
//  float3 lightvec = normalize(lightpos3 - worldposition);
float4 lightvec = normalize(lightpos - worldposition4);
lightvec = -lightvec;
float lightintensity = saturate(dot(norm, lightvec)); // calculate the amount of light

// SPECULAR LIGHT
//float3 camposition3 = camposition.xyz;
float3 camvec = normalize(camposition - worldposition4);
camvec = -camvec;
float3 reflectedlight = reflect(lightvec, norm);

float specularfactor = saturate(dot(reflectedlight, camvec));
float dampedfactor = pow(specularfactor, specularpow);
float4 finalspecular = dampedfactor * specularcol;

// COMBINE ALL LIGTHS TOGETHER
color += diffusecol * lightintensity + finalspecular;
output.color = saturate(color);

// TEXTURE
output.texcoord = texcoord;

return output;
}

float4 PShader(float4 color : COLOR, float2 texcoord : TEXCOORD) : SV_TARGET
{
return color *Texture.Sample(ss, texcoord);
}


The normals are part of loaded models. The models are loaded using Assimp Importer:

Model::Model(const char* pFile, ID3D11Device* dev) {
const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
_pScene = _imp.ReadFile(pFile, aiProcess_FlipUVs | aiProcess_FixInfacingNormals | aiProcess_MakeLeftHanded |
aiProcess_GenSmoothNormals | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType);
if (!_pScene) {
}else {
if (_pScene->mNumMeshes < 1) {
}
else {
// Process all meshes
for (unsigned int m = 0; m < _pScene->mNumMeshes; m++) {
_pMesh = _pScene->mMeshes[m];
MeshEntry me;
me._baseVertex = Model::_objectVertices.size();
me._baseIndex = _objectIndices.size();
if (_pMesh) {
Model::_objectVertices.reserve(Model::_objectVertices.size() + _pMesh->mNumVertices);
for (unsigned int i = 0; i < _pMesh->mNumVertices; i++) {
const aiVector3D* pPos = &(_pMesh->mVertices[i]);
const aiVector3D* pNormal = (_pMesh->mNormals != nullptr) ? &(_pMesh->mNormals[i]) : &Zero3D;
const aiVector3D* pTexCoord = (_pMesh->HasTextureCoords(0)) ? &(_pMesh->mTextureCoords[0][i]) : &Zero3D;
VERTEX v;
v.pos.x = pPos->x; v.pos.y = pPos->y; v.pos.z = pPos->z;
v.normal.x = pNormal->x; v.normal.y = pNormal->y; v.normal.z = pNormal->z;
//v.normal.x = 1.0f; v.normal.y = 0.0f; v.normal.z = 0.0f;
v.textCoord.x = pTexCoord->x; v.textCoord.y = pTexCoord->y;
Model::_objectVertices.push_back(v);
}

_objectIndices.reserve(_objectIndices.size() + (_pMesh->mNumFaces * 3));
for (unsigned int i = 0; i < _pMesh->mNumFaces; i++) {
if (_pMesh->mFaces[i].mNumIndices == 3) {
_objectIndices.push_back(_pMesh->mFaces[i].mIndices[0]);
_objectIndices.push_back(_pMesh->mFaces[i].mIndices[1]);
_objectIndices.push_back(_pMesh->mFaces[i].mIndices[2]);
me._numIndices += 3;
}
}

// Process material of the mesh
if (_pScene->HasMaterials()) {
aiMaterial* material = _pScene->mMaterials[_pMesh->mMaterialIndex];
aiString aiTextureFile;
me._materialIndex = _pMesh->mMaterialIndex;
if (material->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
material->GetTexture(aiTextureType_DIFFUSE, 0, &aiTextureFile);
// Convert aiString to LPWSTR
size_t size = strlen(aiTextureFile.C_Str()) + 1; // plus null
wchar_t* wcTextureFile = new wchar_t[size];
std::shared_ptr<wchar_t> sp(wcTextureFile, std::default_delete<wchar_t[]>());
size_t outSize;
mbstowcs_s(&outSize, wcTextureFile, size, aiTextureFile.C_Str(), size - 1);
LPWSTR textureFile = wcTextureFile;
CreateWICTextureFromFile(dev, nullptr, textureFile, nullptr, &(me._pTexture), 0);
if (me._pTexture == nullptr) {
CreateDDSTextureFromFile(dev, nullptr, textureFile, nullptr, &(me._pTexture), 0);
}
}
}
// If no texture found, use the default one
if (me._pTexture == nullptr) {
CreateWICTextureFromFile(dev, nullptr, L"models/default.jpg", nullptr, &(me._pTexture), 0);
}
_entries.push_back(me);
}
}
}
}
}


And the D3D scene is initialized by D3D class.

D3D.h

#pragma once

#include <d3d11.h>
#include <directxmath.h>
#include <vector>
#include "RushHour.h"
#include "D3DSupplementary.h"

class D3D {
public:
D3D() = delete;
D3D(HWND hWnd);
D3D(D3D&&) = default;
D3D& operator= (D3D&&) = default;
~D3D();
// D3D must not be copied, as it would release all elements after copy
D3D(const D3D&) = delete;
D3D& operator=(const D3D&) = delete;

void CreateVertexBuffer(std::vector<VERTEX> OurVertices);
void CreateIndexBuffer(std::vector<UINT> OurIndices);

IDXGISwapChain* GetSwapChain() const { return _swapChain; };
IDXGIDevice* GetDXGIDevice() const { return _dxgiDevice; }
ID3D11Device* GetDevice() const { return _dev; }
ID3D11DeviceContext* GetDeviceContext() const { return _devCon; }
ID3D11RenderTargetView* GetBackBuffer() const { return _bBuffer; }
ID3D11DepthStencilView* GetZBuffer() const { return _zBuffer; }
ID3D11Buffer* GetCBuffer() const { return _cBuffer; }
ID3D11Buffer* GetVBuffer() const { return _vBuffer; }
ID3D11Buffer** GetVBufferAddr() { return &(_vBuffer); }
ID3D11Buffer* GetIBuffer() const { return _iBuffer; }
ID3D11InputLayout* GetLayout() const { return _layout; }
ID3D11RasterizerState* GetRState() const { return _rs;  }
ID3D11SamplerState* GetSState() const { return _ss; }
ID3D11SamplerState** GetSStateAddr() { return &(_ss); }

private:
IDXGISwapChain* _swapChain;             // swap chain interface
IDXGIDevice* _dxgiDevice;
ID3D11Device* _dev;                     // device interface
ID3D11DeviceContext* _devCon;           // device context
ID3D11InputLayout* _layout;             // layout
ID3D11RenderTargetView* _bBuffer;       // backbuffer
ID3D11DepthStencilView* _zBuffer;       // depth buffer
ID3D11Buffer* _cBuffer;                 // constant buffer
ID3D11Buffer* _vBuffer;                 // vertex buffer
ID3D11Buffer* _iBuffer;                 // index buffer

// State objects
ID3D11RasterizerState* _rs;            // the default rasterizer state
ID3D11SamplerState* _ss;               // sampler state

void CreateDevice(HWND hWnd);
void CreateDepthBuffer();
void CreateRenderTarget();
void SetViewport();
void CreateConstantBuffer();
void InitRasterizer();
void InitSampler();
};


D3D.cpp:

#include "stdafx.h"
#include <d3dcompiler.h>
#include "CommonException.h"
#include "D3D.h"

D3D::D3D(HWND hWnd) {
// this function initializes and prepares Direct3D for use
CreateDevice(hWnd);
CreateDepthBuffer();
CreateRenderTarget();
SetViewport();
CreateConstantBuffer();
InitRasterizer();
InitSampler();
}

D3D::~D3D() {
// switch to windowed mode
if (_swapChain) _swapChain->SetFullscreenState(FALSE, NULL);

// release all the stuff
if (_zBuffer) _zBuffer->Release();
if (_layout) _layout->Release();
if (_vs) _vs->Release();
if (_ps) _ps->Release();
if (_vBuffer) _vBuffer->Release();
if (_iBuffer) _iBuffer->Release();
if (_cBuffer) _cBuffer->Release();
if (_swapChain) _swapChain->Release();
if (_bBuffer) _bBuffer->Release();
if (_rs) _rs->Release();
if (_ss) _ss->Release();
if (_dev) _dev->Release();
if (_devCon) _devCon->Release();
}

void D3D::CreateDevice(HWND hWnd) {
DXGI_SWAP_CHAIN_DESC scd;
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));

// fill the swap chain description struct
scd.BufferDesc.Width = SCREEN_WIDTH;                    // set the back buffer width
scd.BufferDesc.Height = SCREEN_HEIGHT;                  // set the back buffer height
scd.BufferCount = 1;                                    // one back buffer
scd.BufferDesc.RefreshRate.Numerator = 0;                                   // refresh rate: 0 -> do not care
scd.BufferDesc.RefreshRate.Denominator = 1;
scd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;     // use 32-bit color
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;      // how swap chain is to be used
scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;     // unspecified scan line ordering
scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;                     // unspecified scaling
scd.OutputWindow = hWnd;                                // the window to be used
scd.SampleDesc.Count = 4;                               // how many multisamples
scd.SampleDesc.Quality = 0;
scd.Windowed = RUNINWINDOW;                             // windowed/full-screen mode
scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;     // allow full-screen switching by Alt-Enter

D3D_FEATURE_LEVEL featureLevel;

// create a device, device context and swap chain using the information in the scd struct
if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT /* This flag is necessary for compatibility with Direct2D */,
NULL, NULL, D3D11_SDK_VERSION, &scd, &_swapChain, &_dev, &featureLevel, &_devCon))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create the Direct3D device!");
} else if (featureLevel < D3D_FEATURE_LEVEL_11_0) {
throw CommonException((LPWSTR)L"Critical error: You need DirectX 11.0 or higher to run this game!");
}

if (FAILED(_dev->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<LPVOID*>(&_dxgiDevice)))) {
throw CommonException((LPWSTR)L"Critical error: Unable to get Direct3D DXGI device!");
}
}

void D3D::CreateDepthBuffer() {
// create the depth buffer texture
D3D11_TEXTURE2D_DESC texd;
ZeroMemory(&texd, sizeof(texd));
texd.Width = SCREEN_WIDTH;
texd.Height = SCREEN_HEIGHT;
texd.ArraySize = 1;
texd.MipLevels = 1;
texd.SampleDesc.Count = 4;
texd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
texd.Usage = D3D11_USAGE_DEFAULT;
texd.BindFlags = D3D11_BIND_DEPTH_STENCIL;
ID3D11Texture2D *pDepthBuffer;
if (FAILED(_dev->CreateTexture2D(&texd, NULL, &pDepthBuffer))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D depth buffer texture!");
}

// create the depth buffer
D3D11_DEPTH_STENCIL_VIEW_DESC dsvd;
ZeroMemory(&dsvd, sizeof(dsvd));
//dsvd.Format = DXGI_FORMAT_D32_FLOAT;
dsvd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
dsvd.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
if (FAILED(_dev->CreateDepthStencilView(pDepthBuffer, &dsvd, &_zBuffer))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D depth buffer!");
}
pDepthBuffer->Release();
}

void D3D::CreateRenderTarget() {
// create backbuffer and render target
ID3D11Texture2D *pBackBuffer;
if (FAILED(_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<LPVOID*>(&pBackBuffer)))) {
throw CommonException((LPWSTR)L"Critical error: Unable to get Direct3D depth buffer!");
}
if (FAILED(_dev->CreateRenderTargetView(pBackBuffer, NULL, &_bBuffer))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D depth buffer!");
}
pBackBuffer->Release();
_devCon->OMSetRenderTargets(1, &_bBuffer, _zBuffer);
}

void D3D::SetViewport() {
// set the viewport
D3D11_VIEWPORT viewport;
ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));

viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = SCREEN_WIDTH;
viewport.Height = SCREEN_HEIGHT;
viewport.MinDepth = 0;
viewport.MaxDepth = 1;

}

ID3D10Blob *VS, *PS;
if (FAILED(_dev->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &_vs))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D vertex shader!");
}
if (FAILED(_dev->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &_ps))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D pixel shader!");
}

// create the input layout object
D3D11_INPUT_ELEMENT_DESC ied[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

if (FAILED(_dev->CreateInputLayout(ied, 3, VS->GetBufferPointer(), VS->GetBufferSize(), &_layout))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D input layout!");
}
_devCon->IASetInputLayout(_layout);
}

void D3D::CreateConstantBuffer() {
// constant buffer
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(CBUFFER);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
if (FAILED(_dev->CreateBuffer(&bd, NULL, &_cBuffer))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D constant buffer!");
}
_devCon->VSSetConstantBuffers(0, 1, &_cBuffer);
}

void D3D::CreateVertexBuffer(std::vector<VERTEX> OurVertices) {
// create the vertex buffer
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(VERTEX) * OurVertices.size();
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (FAILED(_dev->CreateBuffer(&bd, NULL, &_vBuffer))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D vertex buffer!");
}

// copy the vertices into the buffer
D3D11_MAPPED_SUBRESOURCE ms;
if (FAILED(_devCon->Map(_vBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms))) {
throw CommonException((LPWSTR)L"Critical error: Unable to map Direct3D vertex buffer!");
}
memcpy(ms.pData, OurVertices.data(), sizeof(VERTEX) * OurVertices.size());
_devCon->Unmap(_vBuffer, NULL);
}

void D3D::CreateIndexBuffer(std::vector<UINT> OurIndices) {
// create the index buffer
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(UINT) * OurIndices.size();
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bd.MiscFlags = 0;
if (FAILED(_dev->CreateBuffer(&bd, NULL, &_iBuffer))) {
throw CommonException((LPWSTR)L"Critical error: Unable to create Direct3D index buffer!");
}

// copy the indices into the buffer
D3D11_MAPPED_SUBRESOURCE ms;
if (FAILED(_devCon->Map(_iBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms))) {
throw CommonException((LPWSTR)L"Critical error: Unable to map Direct3D index buffer!");
}
memcpy(ms.pData, OurIndices.data(), sizeof(UINT) * OurIndices.size());
_devCon->Unmap(_iBuffer, NULL);
}

void D3D::InitRasterizer() {
D3D11_RASTERIZER_DESC rd;
rd.FillMode = D3D11_FILL_SOLID;
rd.CullMode = D3D11_CULL_BACK;
rd.FrontCounterClockwise = TRUE;
rd.DepthClipEnable = TRUE;
rd.ScissorEnable = FALSE;
rd.AntialiasedLineEnable = FALSE;
rd.MultisampleEnable = FALSE;
rd.DepthBias = 0;
rd.DepthBiasClamp = 0.0f;
rd.SlopeScaledDepthBias = 0.0f;
if (FAILED(_dev->CreateRasterizerState(&rd, &_rs))) {
throw CommonException((LPWSTR)L"Critical error: Unable to creat Direct3D rasterizer state!");
}
}

void D3D::InitSampler() {
D3D11_SAMPLER_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sd.MaxAnisotropy = 16;
sd.BorderColor[0] = 0.0f;
sd.BorderColor[1] = 0.0f;
sd.BorderColor[2] = 0.0f;
sd.BorderColor[3] = 0.0f;
sd.ComparisonFunc = D3D11_COMPARISON_NEVER;
sd.MinLOD = 0.0f;
sd.MaxLOD = FLT_MAX;
sd.MipLODBias = 0.0f;
if (FAILED(_dev->CreateSamplerState(&sd, &_ss))) {
throw CommonException((LPWSTR)L"Critical error: Unable to creat Direct3D sampler state!");
}
}


I hope I have provided all important parts of the code. Just for case, the complete code can be found here.

Another thing I don't understand in my app might not be connected with my problem at all, but maybe it is connected so I rather mention it here:

Transformation matrices are ok to be stored into constant buffer in original form (== not transposed). As far as I understood from articles I found (e.g. here) the CPU stores arrays in row-major format while HLSL needs them in column-major format so I must transpose all matrices before I store them into the constant buffer. But in my project it works without transposed matrices. When I transpose them, the scene is completely broken.

• Why do you compute your world position twice, once with translation (float4 worldposition4 = mul(world, position);), and once without (float3 worldposition = mul((float3x3)world, position.xyz);)? Commented Apr 1, 2018 at 21:58
• I don't see where you are calculating your diffuse light, which is usually the dot of the surface normal with the diffuse light direction. All you are doing is multiplying the diffusecolour by your point light vector. You need to multiply by the diffuselight direction. That's very different to the point light. Commented Apr 1, 2018 at 22:07
• @ErnieDingo - it is done in the HLSL code float4 lightvec = normalize(lightpos - worldposition4); lightvec = -lightvec; float lightintensity = saturate(dot(norm, lightvec)); // calculate the amount of light color += diffusecol * lightintensity + finalspecular; output.color = saturate(color); Commented Apr 1, 2018 at 22:10
• @DMGregory - I was just trying whether it makes any difference - it does not. It is just a remain of my experiments. Commented Apr 1, 2018 at 22:17
• your light intensity for a diffuse light should be a static single direction vector that does not get calculated by the position of the vertex. Commented Apr 1, 2018 at 22:20

Your "world" matrix isn't really a world matrix. It's a world/model view projection matrix. That means it varies with your viewpoint, causing all your lighting calculations that depend on it to also vary with viewpoint.

You'll want to send (up to) three matrices down the pipe:

1. Object-to-World matrix, transforming model space positions to world space.

Use this matrix to get your world positions for comparing against your worldspace light & camera positions for your worldspace lighting calculations.

2. Model View Projection or MVP matrix, transforming model space positions into world space, into view space, and ultimately into perspective projection space.

Use this matrix (or optionally a View-Projection matrix multiplied by the worldspace position calculated in step 1) to produce your SV_POSITION for projecting your vertices onto the screen.

3. Inverse Transpose of 1, if you're using non-uniform scales anywhere.

Use this to transform your normal vectors. If all your scales are uniform, then you can re-use the World matrix in step 1 for this task too.

• Thank you. I have edited the code (see the edit - Render function and HLSL) to provide three matrices - world, mvp and inverted transposed world. The scene changed, but still not ok. I must play with that and then will let you know. Commented Apr 1, 2018 at 22:47
• Not much I can do with "not ok" as the only symptom. Remember we can't see what you see, so the more information you can give us, the better we can help. At a glance though, you probably want to strip off the w component and work in 3 dimensions only after you've transformed your world position. The fourth component helps for matrix multiplications but can trip you up elsewhere. Taking the camera direction as a 4-vector and normalizing it is not necessarily meaningful, for instance. Commented Apr 1, 2018 at 22:48
• "Not ok" situation was caused by wrong specular light. When I disable it, the scene looks much better now. Tomorrow I will try to play with that again (it is early morning here and I finally must go to bed :) and let you know. Thank you very much for your help. So far it looks it might solve my problem. Commented Apr 1, 2018 at 23:00