Heightmap aus BMP (DirectX 10)

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

Hallo
Ich habe mich seit langer Zeit mal wieder auf meine C++ IDE gestürzt und versucht mich in DirectX 10 einzuarbeiten.
Dank einiger guter Tutorials, der Doku und gewaltig viel Frustrationstoleranz habe ich bisher auch alles umsetzen können was ich mir vorgestellt habe.
Bei meinem jetzigen Problem hänge ich allerdings gewaltig, weil ich keinen anständigen Ansatz habe es umzusetzen.

Ich habe einen kleinen Algorithmus der mir in einem quadratischen Gitter angeordnete Vertices erzeugt, und passende Indices um ein flaches "Terrain" zu rendern, sprich einfach nur eine grosse Fläche bestehend aus unnötig vielen Dreiecken.

Nun möchte ich den position.y Wert anhand einer "Heightmap" modulieren, sprich ein 24bit bmp File laden und entsprechend der Graustufe die Höhen anpassen.
Und genau da häng ich....
Ich hab das selbe mal mit DirectX 8 und der D3DX-Bibiothek gemacht, aber ich kann es einfach nicht portieren.

Das ist momentan mein Ansatz (und ich hatte schon etliche davor, die aber alle nicht funktioniert haben)

Code: Alles auswählen

// heightmap laden
	ID3D10Resource* heightmaptex;
	D3DX10CreateTextureFromFile(m_device, L"heightmap.bmp", NULL, NULL, &heightmaptex, NULL);
Ich bitte euch nun also um Hilfe, wie kann ich aus dieser geladenen Textur die Graustufen herrauslesen?
Ich würde wirklich nicht fragen wenn ich nicht schon vieles vergeblich versucht habe :roll:
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Heightmap aus BMP (DirectX 10)

Beitrag von eXile »

Wichtig ist erst einmal, dass du beim Erstellen D3D10_USAGE_STAGING in der D3DX10_IMAGE_LOAD_INFO angibst. Nach dem Laden Mapst du die Textur mit D3D10_MAP_READ, und hast dann im Parameter pMappedTex2D die Pixeldaten stehen.

Ich würde aber eher das allgemeine Vorgehen überdenken: Da du wohl eher das Terrain im Vertex-Shader verändern willst, wäre es vielleicht sinnvoller, die Textur auf die GPU zu laden, anstatt vortransformierte Vertices zu verwenden. Aber das hängt vom Anwendungsfall ab.
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

Hi!
Danke für die schnelle Antwort.

D3D10_USAGE_STAGING benötige ich, so nehme ich an, dass ich via CPU auf der Textur lesen kann (was ich ja tun möchte), alle anderen Parameter incl. dem Standard scheinen das auszuschliessen.
Korrigiere mich bitte wenn ich das falsch verstanden habe.

Ich würde die Textur nun gerne Mappen, allerdings existiert keine Funktion ID3D10Resource::Map(), allerdings existiert ID3D10Texture2D::Map(), Problem dabei ist nun allerdings dass D3DX10CreateTextureFromFile nunmal explizit einen Pointer auf eine ID3D10Resource zurückgibt.
Gehe ich richtig in der Annahme dass ich das irgendwie ineinander überführen muss?

Noch eine Frage:
Was habe ich für einen Vorteil die Höhendaten erst im Vertexshader einfliessen zu lassen, schliesslich soll es ja ein statisches Terrain sein.
Ich nehme an ich würde dass für solche Dinge wie Wasser benötigen? In welchen Situationen ist es noch von Vorteil?
Altair
Beiträge: 26
Registriert: 28.06.2010, 15:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Altair »

Du kannst ganz einfach:

Code: Alles auswählen

ID3D10Texture2D* heightmaptex;
D3DX10CreateTextureFromFile(m_device, L"heightmap.bmp", NULL, NULL, (ID3D10Resource**)&heightmaptex, NULL);
schreiben. Denn ID3D10Texture ist von ID3D10Resource abgeleitet und deswegen kannst du das umcasten.

Mfg Altair
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Heightmap aus BMP (DirectX 10)

Beitrag von eXile »

Jonsc1 hat geschrieben:Was habe ich für einen Vorteil die Höhendaten erst im Vertexshader einfliessen zu lassen, schliesslich soll es ja ein statisches Terrain sein.
Ich nehme an ich würde dass für solche Dinge wie Wasser benötigen? In welchen Situationen ist es noch von Vorteil?
Für ein statisches Terrain alleine ist das eine legitime Lösung! Aber ganz generell gibt es Situationen beim Rendern von vollkommen anderen Objekten, wo du auf die Höheninformationen aus der Heightmap zugreifen willst: Wie du schon gesagt hast, wenn du Wasser rendern möchtest, und dieses in z.B. tiefen Regionen dunkler erscheinen soll. Oder wenn du ein Partikelsystem einbaust, das auf dem Terrain arbeitet. Oder einen Nebel in tieferen Regionen malen willst. Oder ein komplexes Animationssystem, bei welchem du die Füße von Personen genau auf dem Terrain ausrichten möchtest, und keine schwebenden Füße haben willst. Oder ganz allgemein: Es hält dir mehr Möglichkeiten offen ;)
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

Ich habe halt immer Angst dass sowas sehr zu Lasten der Performance geht, aber ich nehme an das die Hardware mit ein paar extra Multiplikationen mittlerweile kein Problem mehr hat :)

Denke das Prinzip ist die Heightmap als Shader Resource zu laden und dann im Vertexshader die Farbwerte in den position.y Wert einzurechnen... nur wie mach ich das?
Bin nicht besonders gut mit HLSL, hab die Textur nun Geladen und im Pixelshader auch Zugriff darauf, aber im Vertexshader? :/
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Heightmap aus BMP (DirectX 10)

Beitrag von eXile »

Ich sehe da prinzipiell zwei Möglichkeiten: Einmal via Vertex Texture Fetch im Vertex-Shader (SM 3.0) oder einen Geometry-Shader benutzen (SM 4.0). Meiner Meinung nach (hoffentlich stimmt das auch alles) würde ich letzteres benutzen. Eine "Standard-Möglichkeit" wäre auch gleich die benutzung von Clipmaps, siehe hier (das benutzt auch gleich Vertex Texture Fetch). Im Forum gabs dazu auch schon einmal einen Thread, siehe hier.

Oder zu bleibst einfach bei deiner statischen Geometrie ;)
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

Sorry wenn ich so dumm frage, aber ich bekomm es einfach nicht hin.
Mein Shader kompiliert einfach nicht, was daran liegt dass ich es anscheinend nicht richtig verstehe.

tex2Dlod nimmt garkeinen Bezug auf irgendeine Textur, ich verstehe die Funktionsweise vorne und hinten nicht.
Wenn ich im Pixelshader auf meine Textur bezug nehme dann so:
Texture.Sample(ss, Tex);

Aber im Vertexshader? Kann mir da jemand ein Codeschnipsel posten?
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Krishty »

SampleLevel()
Ich habe auch ehrlich gesagt nie verstanden, wo man die tex*-Funktionen einsetzen kann.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

Habs jetzt mal nicht mit SampleLevel() versucht sonder mit tex2Dlod(), Problem ist nur, sobald ich die Kommentierung der Funktion wegnehme und versuche sie auszuführen, wird der Shader nicht mehr kompiliert

Code: Alles auswählen

float4x4 WorldMat;
float4x4 CamMat;

Texture2D Heightmap;

SamplerState displacementSampler     
{
Texture = (Heightmap);
AddressU = Clamp;
AddressV = Clamp;
};

// Vertexshader - Rückgabe
struct VSOut
{
    float4 Col : COLOR;              // vertex farbe
    float2 Tex : TEXCOORD;       // texturkoordinaten
    float4 Pos : SV_POSITION;    // vertex position
};

// Vertexshader
VSOut VS(float4 Pos : POSITION, float4 Col : COLOR,  float2 Tex : TEXCOORD)
{
    VSOut Output;
    //float4 height = tex2Dlod( displacementSampler, float4(Tex.x , Tex.y , 0,0  ) );

    Output.Pos = mul(Pos, WorldMat);          // World-Matrix-Transformation
    Output.Pos = mul(Output.Pos, CamMat); // Kameratransformation

    Output.Col = Col;    //Farbe unverändert lassen
    Output.Tex = Tex;  // Texturkoordinaten unverändert lassen

    return Output;    // -> Rasterizer Stage
}
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

Hat keiner eine Antwort warum das nicht kompiliert?
Ich bin mittlerweile soweit das mir Google wieder diesen Thread hier ausspuckt...
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Krishty »

Jonsc1 hat geschrieben:Hat keiner eine Antwort warum das nicht kompiliert?
Weil du tex2Dlod() statt SampleLevel() benutzt?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

Ich sollte mehr auf die Leute hören :)
So funktionierts tatsächlich:

Code: Alles auswählen

float4x4 WorldMat;
float4x4 CamMat;
Texture2D Heightmap;

sampler displacementSampler    //this sampler will be used to read (sample) the heightmap
{
AddressU = Clamp;
AddressV = Clamp;
};

// a struct for the vertex shader return value
struct VSOut
{
    float4 Col : COLOR;    // vertex color
    float2 Tex : TEXCOORD;       // texturkoordinaten
    float4 Pos : SV_POSITION;    // vertex screen coordinates
};

// the vertex shader
VSOut VS(float4 Pos : POSITION, float4 Col : COLOR,  float2 Tex : TEXCOORD)
{
    VSOut Output;
    float height = Heightmap.SampleLevel ( displacementSampler, Tex, 0 ).g; 

    Output.Pos = mul(Pos, WorldMat);    //World Transformation
    Output.Pos = mul(Output.Pos, CamMat); //View + Projection -Transformation

    Output.Pos.y = Output.Pos.y + height * 50; 

    Output.Col = Col;    // set the vertex color to the input's color
    Output.Tex = Tex;

    return Output;    //-> Rasterizer Stage
}
Danke an alle :)
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Krishty »

Schön, das zu hören. Wenn wir schonmal da beisind: Warum existieren die tex*()-Funktionen? Haben die sich jemals irgendwo irgendwie kompilieren lassen?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

Wenn ich das im FXComposer compilieren will (also mit der tex2Lod Funktion), bekomme ich ne Meldung von wegen dafür müsste Abwärtskompatibilität aktiviert werden... event. laufen die nur unter VS_3.0... auch wenn ich dann den Grund nicht verstehe.
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Krishty »

Hm. Ich bin mir sicher, auch mit SM 2.0 immer die Sample*()-Funktionen benutzt zu haben, damit riecht die Sache eher nach SM 1.x-Legacy :/ Wahrscheinlich, weil Methoden in HLSL noch relativ neu sind …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

So, ich habe wie empfohlen die ganzen Berechnungen im Vertexshader gemacht, und es hat sich gezeigt dass es wirklich sehr sinnvoll war, da ich so die Höhendaten zur Verfügung hatte um z.B. dynamisch zu Texturieren.
Bild

Das funktioniert also soweit.

Nun benötige ich allerdings die Höhendaten auch auf der CPU, und möchte sie mir in ein Array speichern.
Allerdings lese ich anscheinen nur Mist aus, die ausgelesenen Floats sind negativ (erwartet hätte ich Werte zwischen 0 und 1 -> rgb Farben), manchmal erhalte ich auch NAN als Meldung.
Sieht also irgendwie so aus als würde ich an der falschen Stelle im Speicher lesen, aber ich habe mich eigentlich recht genau an das Beispiel aus der MSDN gehalten:
http://msdn.microsoft.com/en-us/library ... 85%29.aspx

Es tut mir leid dass der Code nicht anständig formatiert ist, aber das scheint ein Problem mit dem Forum hier zu sein... eigentlich ist er eingerückt

Code: Alles auswählen

	HRESULT hr;
	D3DX10_IMAGE_LOAD_INFO loadinfo;
	ID3D10Texture2D* heightmaptex;
	D3D10_MAPPED_TEXTURE2D mapped;
	
	loadinfo.Usage = D3D10_USAGE_STAGING;
	loadinfo.CpuAccessFlags = D3D10_CPU_ACCESS_READ;
	loadinfo.BindFlags = 0;

	
	hr = D3DX10CreateTextureFromFile(m_pDevice, L"heightmap.jpg", &loadinfo, NULL, (ID3D10Resource**)&heightmaptex, NULL);
	if(FAILED(hr))
	{
		std::string errdesc = ws2s(DXGetErrorDescription(hr)); 
		throw ISEN_Exception(__FILE__, __LINE__, "D3DX10CreateTextureFromFile", errdesc);
	}

	hr = heightmaptex->Map(0, D3D10_MAP_READ,0, &mapped);
	if(FAILED(hr))
	{
		std::string errdesc = ws2s(DXGetErrorDescription(hr)); 
		throw ISEN_Exception(__FILE__, __LINE__, "heightmaptex->Map", errdesc);
	}
	
	D3D10_TEXTURE2D_DESC desc;
    heightmaptex->GetDesc( &desc );


    const UINT WIDTH = desc.Width;
    const UINT HEIGHT = desc.Height;
	
    FLOAT* pTexels = (FLOAT*)mapped.pData;
    for( UINT row = 0; row < HEIGHT; row++ )
    {
      UINT rowStart = row * mapped.RowPitch/4;
      for( UINT col = 0; col < WIDTH; col++ )
      {
		int test = 0;
        test = pTexels[rowStart + col*4 + 0];	// Red
        pTexels[rowStart + col*4 + 1];			// Green
        pTexels[rowStart + col*4 + 2];			// Blue
        pTexels[rowStart + col*4 + 3];			// Alpha

		MessageBox(0, s2ws(floatToString(test)).c_str() ,L"-",1);
      }
    }


	 heightmaptex->Unmap(0);
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Krishty »

So weit ich sehe, gibst du beim Laden der ursprünglichen Textur kein Format an, darum erzeugt DXGI die Textur in dem Format, was am nächsten an dem der Bilddatei liegt, und das ist R8G8B8X8_UNORM.

Du musst die Höhendaten also als Array von Höhe × Breite × RGBx unsigned chars interpretieren …

… aber damit ist es natürlich nicht getan, denn wenn jetzt z.B. jemand die BMP von RGB auf Graustufen umstellt liest du dreifach über den Speicher hinaus.

Und das ist dann der Punkt, wo du deine Heightmap manuell aus welchem Bildformat auch immer laden solltest, sie in das Format konvertierst, das du dir dafür überlegt hast (wenn es nur 256 Höhenabstufungen gibt (mehr kriegst du ja in eine BMP eh nicht rein) ist R8_UNORM okay, falls du mit R32_FLOAT besser arbeiten und den vierfachen Speicherverbrauch ohne Mehrwert in Kauf nehmen kannst, nimm eben floats) und direkt per D3D11 eine Textur aus diesen Daten erzeugst. Dann hast du nämlich auch keinen GPU-Download mehr.

Oder, einfacher: du gibst der DXGI-Funktion feste Parameter vor, mit denen sie die Textur zu laden hat und gehst beim Mappen der Textur sicher, dass sie in diesem Format vorliegt.

Und den Code musst du mit code=cpp ankündigen, dann wird er auch formatiert. Wir haben mal versucht, das als Standardeinstellung zu setzen, sind aber gescheitert -.-
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Jonsc1
Beiträge: 19
Registriert: 14.08.2006, 18:51

Re: Heightmap aus BMP (DirectX 10)

Beitrag von Jonsc1 »

Danke, genau da lag das Problem.
Endlich hab ich das mit den Formaten mal richtig verstanden :)
Antworten