Sign in to follow this  
JonW

*Really* crazy texture sampling issue in D3D 10

Recommended Posts

Hey folks,

I have an issue with a Direct3D 10 application that has had me tearing my hair out for many many hours. I have broken the problem down into it's near-bare essense in a small program based off of the Microsoft tutorials.

This program consists of a simple pixel shader that draws a blue circle on a blue background using 2 samples from a 512x512 texture. The pixel shader code looks like this:


Texture2D shdrTexture;

SamplerState linearSampler
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};

//
// Vertex Shader
//
float4 VS( float4 Pos : POSITION ) : SV_POSITION
{
return Pos;
}

//
// Pixel Shader
//
float4 PS( float4 Pos : SV_POSITION ) : SV_Target
{
// Draw a circle at position 200,200 with radius of 100.
Pos.x -= 200;
Pos.y -= 200;
float rad = sqrt(Pos.x*Pos.x + Pos.y*Pos.y);

// Draw with one texture color or the other.
float2 texCoord;
if (rad > 100) texCoord = float2( 0.19921875f, 0.298828125f );
else texCoord = float2( 0.439453125f, 0.369140625f );

float4 clr = shdrTexture.Sample(linearSampler, texCoord);
return clr;
}

technique10 Render
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_4_0, PS() ) );
}
}




The image, however, turns out like this:



As you can see, even though we are only sampling 2 pixels, the circle ends up with a white border.

The remainder setup code for the program looks like this, in its entirety:



#include <Windows.h>
#include <d3d10.h>
#include <d3dx10.h>

//--------------------------------------------------------------------------------------
// Structures
//--------------------------------------------------------------------------------------
struct SimpleVertex
{
D3DXVECTOR3 Pos;
};

//--------------------------------------------------------------------------------------
// Forward declarations
//--------------------------------------------------------------------------------------
HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow);
HRESULT InitDevice();
HRESULT InitRender();
void Cleanup();
void Render();
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//--------------------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------------------
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;
D3D10_DRIVER_TYPE g_driverType = D3D10_DRIVER_TYPE_NULL;
ID3D10Device* g_pd3dDevice = NULL;
IDXGISwapChain* g_pSwapChain = NULL;
ID3D10RenderTargetView* g_pRenderTargetView = NULL;

ID3D10Effect* g_pEffect = NULL;
ID3D10EffectTechnique* g_pTechnique = NULL;
ID3D10InputLayout* g_pVertexLayout = NULL;
ID3D10Buffer* g_pVertexBuffer = NULL;

ID3D10ShaderResourceView* g_pTextureRV = NULL;
ID3D10EffectShaderResourceVariable* g_pTexture = NULL;

//--------------------------------------------------------------------------------------
// Entry point to the program. Initializes everything and goes into a message processing
// loop. Idle time is used to render the scene.
//--------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

if(FAILED(InitWindow(hInstance, nCmdShow)))
return 0;

if(FAILED(InitDevice()))
{
Cleanup();
return 0;
}

if (FAILED(InitRender()))
{
Cleanup();
return 0;
}

// Main message loop
MSG msg = {0};
while(WM_QUIT != msg.message)
{
// Give all messages that need to be processed priority over the rendering cycle.
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Render();
}
}

Cleanup();

return (int)msg.wParam;
}

//--------------------------------------------------------------------------------------
// Register class and create window
//--------------------------------------------------------------------------------------
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow )
{
// Register class
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = L"SDRWINDOW";
wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
if( !RegisterClassEx( &wc ) )
return E_FAIL;

// Create window
g_hInst = hInstance;
RECT rc = { 0, 0, 400, 400 };
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
g_hWnd = CreateWindow( L"SDRWINDOW", L"Pixel Shader Test", 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;
}

//--------------------------------------------------------------------------------------
// Create Direct3D device and swap chain
//--------------------------------------------------------------------------------------
HRESULT InitDevice()
{
// Create a device and a swap chain. The device object is used to perform rendering onto a buffer,
// and also contains methods to create resources. The swap chain takes the buffer that the device
// renders to an displays it on the monitor screen. It swaps the buffers so that there is always
// one read-only buffer that is actually on the screen.

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;
#ifdef _DEBUG
createDeviceFlags |= D3D10_CREATE_DEVICE_DEBUG;
#endif

D3D10_DRIVER_TYPE driverTypes[] =
{
D3D10_DRIVER_TYPE_HARDWARE,
D3D10_DRIVER_TYPE_REFERENCE,
};
UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0] );

DXGI_SWAP_CHAIN_DESC sd;
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 = 60;
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;

for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
g_driverType = driverTypes[driverTypeIndex];
hr = D3D10CreateDeviceAndSwapChain( NULL, g_driverType, NULL, createDeviceFlags,
D3D10_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice );
if( SUCCEEDED( hr ) )
break;
}
if( FAILED( hr ) )
return hr;

// Create a render target view
ID3D10Texture2D* pBackBuffer;
hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), ( LPVOID* )&pBackBuffer );
if( FAILED( hr ) )
return hr;

hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );
pBackBuffer->Release();
if( FAILED( hr ) )
return hr;

g_pd3dDevice->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL );

// Setup the viewport
D3D10_VIEWPORT vp;
vp.Width = width;
vp.Height = height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pd3dDevice->RSSetViewports( 1, &vp );

return S_OK;
}

//--------------------------------------------------------------------------------------
// Create vertex buffers and shaders.
//--------------------------------------------------------------------------------------
HRESULT InitRender()
{
HRESULT hr = S_OK;

// Create the effect

DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;

hr = D3DX10CreateEffectFromFile( L"Shader.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0,
g_pd3dDevice, NULL, NULL, &g_pEffect, NULL, NULL );
if( FAILED( hr ) )
{
MessageBox( NULL, L"Couldn't load the FX file!", L"Error", MB_OK );
return hr;
}

// Obtain the technique defined in the effect file
g_pTechnique = g_pEffect->GetTechniqueByName( "Render" );

// Obtain the variables
g_pTexture = g_pEffect->GetVariableByName("shdrTexture")->AsShaderResource();

// Define the input layout
D3D10_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = sizeof( layout ) / sizeof( layout[0] );

// Create the input layout
D3D10_PASS_DESC PassDesc;
g_pTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc );
hr = g_pd3dDevice->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature,
PassDesc.IAInputSignatureSize, &g_pVertexLayout );
if( FAILED( hr ) )
return hr;

// Set the input layout
g_pd3dDevice->IASetInputLayout( g_pVertexLayout );

// Create vertex buffer
SimpleVertex vertices[] =
{
D3DXVECTOR3( -1, -1, 0 ),
D3DXVECTOR3( -1, 1, 0 ),
D3DXVECTOR3( 1, -1, 0 ),
D3DXVECTOR3( 1, 1, 0)
};
D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleVertex ) * 4;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices;
hr = g_pd3dDevice->CreateBuffer( &bd, &InitData, &g_pVertexBuffer );
if( FAILED( hr ) )
return hr;

// Set vertex buffer
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
g_pd3dDevice->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );

// Set primitive topology
g_pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

// Formats supported by texture creation function: BMP, JPG, PNG, DDS, TIFF, GIF, WMP
hr = D3DX10CreateShaderResourceViewFromFile(g_pd3dDevice, L"shader.bmp", NULL, NULL, &g_pTextureRV, NULL);
if (FAILED(hr))
return hr;

// Set the map texture variable
hr = g_pTexture->SetResource(g_pTextureRV);
if (FAILED(hr))
return hr;

return S_OK;
}

//--------------------------------------------------------------------------------------
// Render the frame
//--------------------------------------------------------------------------------------
void Render()
{
// Clear the back buffer
float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; // red,green,blue,alpha
g_pd3dDevice->ClearRenderTargetView( g_pRenderTargetView, ClearColor );

// Render a triangle
D3D10_TECHNIQUE_DESC techDesc;
g_pTechnique->GetDesc( &techDesc );
for( UINT p = 0; p < techDesc.Passes; ++p )
{
// Bind the shaders for this pass to stages in the pipeline
g_pTechnique->GetPassByIndex( p )->Apply( 0 );

// Process the vertex data passed into the pipeline
g_pd3dDevice->Draw( 4, 0 );
}

// Present the information rendered to the back buffer to the front buffer (the screen)
g_pSwapChain->Present( 0, 0 );
}


//--------------------------------------------------------------------------------------
// Clean up the objects we've created
//--------------------------------------------------------------------------------------
void Cleanup()
{
if( g_pd3dDevice ) g_pd3dDevice->ClearState();

if( g_pVertexBuffer ) g_pVertexBuffer->Release();
if( g_pVertexLayout ) g_pVertexLayout->Release();
if (g_pTextureRV ) g_pTextureRV->Release();
if( g_pEffect ) g_pEffect->Release();
if( g_pRenderTargetView ) g_pRenderTargetView->Release();
if( g_pSwapChain ) g_pSwapChain->Release();
if( g_pd3dDevice ) g_pd3dDevice->Release();
}

//--------------------------------------------------------------------------------------
// Called every time the application receives a message
//--------------------------------------------------------------------------------------
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;
}




Here is the test texture that I am using (use "Save Target As..."): shader.bmp

The two colors are coming from the 2 dark regions of the texture. The circle's outline is still white even if the texture's white color is changed.

So as you can see, the texture "Sample" function is returning a different color for the same texel depending on what pixels have already been drawn!! It is the weirdest thing, and I can't even theorize what might be going on here.

I would greatly appreciate any insight that could be provided. If you have any idea at all at what might be wrong, that would be more information that I already have :-D

Thanks!!

Share this post


Link to post
Share on other sites
Erik, I really cannot thank you enough. That is exactly what I was looking for.

I modified the shader resource view creation like so:


D3DX10_IMAGE_INFO InfoFromFile;
D3DX10_IMAGE_LOAD_INFO LoadImageInfo;

// Read info from the file
D3DX10GetImageInfoFromFile(L"shader.bmp", NULL, &InfoFromFile);
LoadImageInfo.Width = InfoFromFile.Width;
LoadImageInfo.Height = InfoFromFile.Height;
LoadImageInfo.Depth = InfoFromFile.Depth;
LoadImageInfo.FirstMipLevel = 0;
LoadImageInfo.MipLevels = 1;
LoadImageInfo.Usage = D3D10_USAGE_DEFAULT;
LoadImageInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE ;
LoadImageInfo.CpuAccessFlags = 0;
LoadImageInfo.MiscFlags = 0;
LoadImageInfo.Format = InfoFromFile.Format;
LoadImageInfo.Filter = D3DX10_FILTER_NONE;
LoadImageInfo.MipFilter = D3DX10_FILTER_NONE;
LoadImageInfo.pSrcInfo = &InfoFromFile;

// Formats supported by texture creation function: BMP, JPG, PNG, DDS, TIFF, GIF, WMP
hr = D3DX10CreateShaderResourceViewFromFile(g_pd3dDevice, L"shader.bmp", &LoadImageInfo, NULL, &g_pTextureRV, NULL);
if (FAILED(hr))
return hr;



...and the texture now samples correctly (I know, it's not good code... I'm beat down though.)

You are a godsend!! And thanks also to everyone else who may have looked at this.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this