*Really* crazy texture sampling issue in D3D 10

Started by
1 comment, last by JonW 13 years, 7 months ago
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!!
Advertisement
It seems to be because of mip-maps. Force the texture to only load with one mip-map level, or use SampleLevel(sampler, texCoord, 0.0f) in the shader.
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.

This topic is closed to new replies.

Advertisement