I never thought about simply rendering a cube instead of a fullscreen rect. But if I do so and turn off culling, won't most of the pixels be rendered twice?
Besides, I fixed the problem by dividing the cubes into 4x4x4 smaller cubes to compute the scissor rect. It may not be 100% correct, but I could not find any error until now.
Edit: There are still some errors, but now they occur when a cube is far away and only at some minimum vertical camera angle. Here is the code, maybe this could help identifying the problem.
//...
D3D11_RECT ScissorRect;
bool ScissorRectIsValid = false;
LONG2 RenderChunkPos = GetRenderChunkPosition(ChunkContainer.Position, UpdatedPlayerPosition.xz());
const float ChunkSizeBias = 0.5f;
#define DIVIDE_CHUNK_COUNT 4
for (int n = 0; n < 8; ++n){
for (int x = 0; x < DIVIDE_CHUNK_COUNT; ++x){
for (int y = 0; y < DIVIDE_CHUNK_COUNT; ++y){
for (int z = 0; z < DIVIDE_CHUNK_COUNT; ++z){
XMFLOAT3 ChunkPos(RenderChunkPos.x + x*(CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT), ChunkContainer.Chunks[8 * k + n].Position.y + y*(MINI_CHUNK_SIZE_Y / DIVIDE_CHUNK_COUNT), RenderChunkPos.y + z*(CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT));
if (ViewingFrustum->Cuboid(XMFLOAT3(ChunkPos.x + CHUNK_SIZE_XZ / 2.0f / DIVIDE_CHUNK_COUNT, ChunkPos.y + MINI_CHUNK_SIZE_Y / 2.0f / DIVIDE_CHUNK_COUNT, ChunkPos.z + CHUNK_SIZE_XZ / 2.0f / DIVIDE_CHUNK_COUNT), XMFLOAT3(CHUNK_SIZE_XZ / 2.0f / DIVIDE_CHUNK_COUNT + ChunkSizeBias, MINI_CHUNK_SIZE_Y / 2.0f / DIVIDE_CHUNK_COUNT + ChunkSizeBias, CHUNK_SIZE_XZ / 2.0f / DIVIDE_CHUNK_COUNT + ChunkSizeBias))){
XMFLOAT4 Edges[8];
Edges[0] = XMFLOAT4(ChunkPos.x + RenderPositionOffset.x - ChunkSizeBias, ChunkPos.y + RenderPositionOffset.y - ChunkSizeBias, ChunkPos.z + RenderPositionOffset.z - ChunkSizeBias, 1.0f);
Edges[1] = XMFLOAT4(ChunkPos.x + RenderPositionOffset.x - ChunkSizeBias, ChunkPos.y + RenderPositionOffset.y - ChunkSizeBias, ChunkPos.z + CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT + RenderPositionOffset.z + ChunkSizeBias, 1.0f);
Edges[2] = XMFLOAT4(ChunkPos.x + RenderPositionOffset.x - ChunkSizeBias, ChunkPos.y + MINI_CHUNK_SIZE_Y / DIVIDE_CHUNK_COUNT + RenderPositionOffset.y + ChunkSizeBias, ChunkPos.z + RenderPositionOffset.z - ChunkSizeBias, 1.0f);
Edges[3] = XMFLOAT4(ChunkPos.x + RenderPositionOffset.x - ChunkSizeBias, ChunkPos.y + MINI_CHUNK_SIZE_Y / DIVIDE_CHUNK_COUNT + RenderPositionOffset.y + ChunkSizeBias, ChunkPos.z + CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT + RenderPositionOffset.z + ChunkSizeBias, 1.0f);
Edges[4] = XMFLOAT4(ChunkPos.x + CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT + RenderPositionOffset.x + ChunkSizeBias, ChunkPos.y + RenderPositionOffset.y - ChunkSizeBias, ChunkPos.z + RenderPositionOffset.z - ChunkSizeBias, 1.0f);
Edges[5] = XMFLOAT4(ChunkPos.x + CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT + RenderPositionOffset.x + ChunkSizeBias, ChunkPos.y + RenderPositionOffset.y - ChunkSizeBias, ChunkPos.z + CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT + RenderPositionOffset.z + ChunkSizeBias, 1.0f);
Edges[6] = XMFLOAT4(ChunkPos.x + CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT + RenderPositionOffset.x + ChunkSizeBias, ChunkPos.y + MINI_CHUNK_SIZE_Y / DIVIDE_CHUNK_COUNT + RenderPositionOffset.y + ChunkSizeBias, ChunkPos.z + RenderPositionOffset.z - ChunkSizeBias, 1.0f);
Edges[7] = XMFLOAT4(ChunkPos.x + CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT + RenderPositionOffset.x + ChunkSizeBias, ChunkPos.y + MINI_CHUNK_SIZE_Y / DIVIDE_CHUNK_COUNT + RenderPositionOffset.y + ChunkSizeBias, ChunkPos.z + CHUNK_SIZE_XZ / DIVIDE_CHUNK_COUNT + RenderPositionOffset.z + ChunkSizeBias, 1.0f);
XMFLOAT2 MinEdge(std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity());
XMFLOAT2 MaxEdge(-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity());
for (int j = 0; j < 8; ++j){
XMFLOAT3 VertexInClipSpace;
XMStoreFloat3(&VertexInClipSpace, XMVector3TransformCoord(XMLoadFloat4(Edges + j), ViewProjectionMatrix));
if (VertexInClipSpace.x < MinEdge.x) MinEdge.x = VertexInClipSpace.x;
if (VertexInClipSpace.x > MaxEdge.x) MaxEdge.x = VertexInClipSpace.x;
if (VertexInClipSpace.y < MinEdge.y) MinEdge.y = VertexInClipSpace.y;
if (VertexInClipSpace.y > MaxEdge.y) MaxEdge.y = VertexInClipSpace.y;
}
if (MinEdge.x <= 1.0f && MinEdge.y <= 1.0f && MaxEdge.x >= -1.0f && MaxEdge.y >= -1.0f){
int32_t ScissorRectLeft = 0,
ScissorRectRight = ScreenWidth,
ScissorRectTop = 0,
ScissorRectBottom = ScreenHeight;
//test if camera is outside the cube
if ((Edges[0].x > 0 || Edges[7].x < 0) || (Edges[0].y > 0 || Edges[7].y < 0) || (Edges[0].z > 0 || Edges[7].z < 0)){
ScissorRectLeft = std::floor((MinEdge.x + 1.0f)*0.5f*ScreenWidth);
ScissorRectRight = std::ceil((MaxEdge.x + 1.0f)*0.5f*ScreenWidth);
ScissorRectTop = std::floor((MinEdge.y + 1.0f)*0.5f*ScreenHeight);
ScissorRectBottom = std::ceil((MaxEdge.y + 1.0f)*0.5f*ScreenHeight);
}
if (ScissorRectIsValid){
ScissorRect.left = std::min((LONG)ScissorRectLeft, ScissorRect.left);
ScissorRect.right = std::max((LONG)ScissorRectRight, ScissorRect.right);
ScissorRect.bottom = std::max((LONG)ScissorRectBottom, ScissorRect.bottom);
ScissorRect.top = std::min((LONG)ScissorRectTop, ScissorRect.top);
}
else{
ScissorRect.left = ScissorRectLeft;
ScissorRect.right = ScissorRectRight;
ScissorRect.bottom = ScissorRectBottom;
ScissorRect.top = ScissorRectTop;
ScissorRectIsValid = true;
}
}
}
}
}
}
}
assert(ScissorRectIsValid);
DeviceContext->RSSetScissorRects(1, &ScissorRect);
//...