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!!