Autor Wątek: Jak renderować realistyczną wodę  (Przeczytany 2774 razy)

Offline baran860

  • Użytkownik

# Lipiec 01, 2014, 21:57:38
Siema, dostałem w swoje ręce source code pewnej gry i postanowiłem sobie ją "udoskonalić" lecz zatrzymałem się na renderingu wody. Część odpowiadająca za jej "renderowanie" wygląda tak:

#include "StdAfx.h"
#include "../eterLib/StateManager.h"
#include "../eterLib/ResourceManager.h"

#include "MapOutdoor.h"
#include "TerrainPatch.h"

void CMapOutdoor::LoadWaterTexture()
{
UnloadWaterTexture();
char buf[256];
for (int i = 0; i < 30; ++i)
{
sprintf(buf, "d:/Game Work/special/water/%02d.dds", i+1);
m_WaterInstances[i].SetImagePointer((CGraphicImage *) CResourceManager::Instance().GetResourcePointer(buf));
}
}

void CMapOutdoor::UnloadWaterTexture()
{
for (int i = 0; i < 30; ++i)
m_WaterInstances[i].Destroy();
}

void CMapOutdoor::RenderWater()
{
if (m_PatchVector.empty())
return;

if (!IsVisiblePart(PART_WATER))
return;

//////////////////////////////////////////////////////////////////////////
// RenderState
D3DXMATRIX matTexTransformWater;

STATEMANAGER.SaveRenderState(D3DRS_ZWRITEENABLE, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
STATEMANAGER.SaveRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
STATEMANAGER.SaveRenderState(D3DRS_COLORVERTEX, TRUE);

STATEMANAGER.SetTexture(0, m_WaterInstances[((ELTimer_GetMSec() / 70) % 30)].GetTexturePointer()->GetD3DTexture());

D3DXMatrixScaling(&matTexTransformWater, m_fWaterTexCoordBase, -m_fWaterTexCoordBase, 0.0f);
D3DXMatrixMultiply(&matTexTransformWater, &m_matViewInverse, &matTexTransformWater);

STATEMANAGER.SaveTransform(D3DTS_TEXTURE0, &matTexTransformWater);
STATEMANAGER.SaveVertexShader(D3DFVF_XYZ|D3DFVF_DIFFUSE);

STATEMANAGER.SaveTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);

STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);


STATEMANAGER.SetTexture(1,NULL);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

// RenderState
//////////////////////////////////////////////////////////////////////////

// ą° Ŕ§ ľĆ·ˇ ľÖ´Ď˝ĂĹ°±â...
static float s_fWaterHeightCurrent = 0;
static float s_fWaterHeightBegin = 0;
static float s_fWaterHeightEnd = 0;
static DWORD s_dwLastHeightChangeTime = CTimer::Instance().GetCurrentMillisecond();
static DWORD s_dwBlendtime = 300;

// 1.5ĂĘ ¸¶´Ů şŻ°ć
if ((CTimer::Instance().GetCurrentMillisecond() - s_dwLastHeightChangeTime) > s_dwBlendtime)
{
s_dwBlendtime = random_range(1000, 3000);

if (s_fWaterHeightEnd == 0)
s_fWaterHeightEnd = -random_range(0, 15);
else
s_fWaterHeightEnd = 0;

s_fWaterHeightBegin = s_fWaterHeightCurrent;
s_dwLastHeightChangeTime = CTimer::Instance().GetCurrentMillisecond();
}

s_fWaterHeightCurrent = s_fWaterHeightBegin + (s_fWaterHeightEnd - s_fWaterHeightBegin) * (float) ((CTimer::Instance().GetCurrentMillisecond() - s_dwLastHeightChangeTime) / (float) s_dwBlendtime);
m_matWorldForCommonUse._43 = s_fWaterHeightCurrent;

m_matWorldForCommonUse._41 = 0.0f;
m_matWorldForCommonUse._42 = 0.0f;
STATEMANAGER.SetTransform(D3DTS_WORLD, &m_matWorldForCommonUse);

float fFogDistance = __GetFogDistance();

std::vector<std::pair<float, long> >::iterator i;

for(i = m_PatchVector.begin();i != m_PatchVector.end(); ++i)
{
if (i->first<fFogDistance)
DrawWater(i->second);
}

STATEMANAGER.SetTexture(0, NULL);
STATEMANAGER.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);

for(i = m_PatchVector.begin();i != m_PatchVector.end(); ++i)
{
if (i->first>=fFogDistance)
DrawWater(i->second);
}

// ·»´ő¸µ ÇŃ ČÄżˇ´Â ą° z Ŕ§Äˇ¸¦ şą±¸
m_matWorldForCommonUse._43 = 0.0f;

//////////////////////////////////////////////////////////////////////////
// RenderState
STATEMANAGER.RestoreVertexShader();
STATEMANAGER.RestoreTransform(D3DTS_TEXTURE0);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_MINFILTER);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_MAGFILTER);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_MIPFILTER);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSU);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSV);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_TEXCOORDINDEX);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS);

STATEMANAGER.RestoreRenderState(D3DRS_DIFFUSEMATERIALSOURCE);
STATEMANAGER.RestoreRenderState(D3DRS_COLORVERTEX);
STATEMANAGER.RestoreRenderState(D3DRS_ZWRITEENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHABLENDENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE);
}

void CMapOutdoor::DrawWater(long patchnum)
{
assert(NULL!=m_pTerrainPatchProxyList);
if (!m_pTerrainPatchProxyList)
return;

CTerrainPatchProxy& rkTerrainPatchProxy = m_pTerrainPatchProxyList[patchnum];

if (!rkTerrainPatchProxy.isUsed())
return;

if (!rkTerrainPatchProxy.isWaterExists())
return;

CGraphicVertexBuffer* pkVB=rkTerrainPatchProxy.GetWaterVertexBufferPointer();
if (!pkVB)
return;

if (!pkVB->GetD3DVertexBuffer())
return;

UINT uPriCount=rkTerrainPatchProxy.GetWaterFaceCount();
if (!uPriCount)
return;

STATEMANAGER.SetStreamSource(0, pkVB->GetD3DVertexBuffer(), sizeof(SWaterVertex));
STATEMANAGER.DrawPrimitive(D3DPT_TRIANGLELIST, 0, uPriCount);

ms_faceCount += uPriCount;
}

Może lekko objaśnie działanie:
na samym początku jest wczytywane 30 obrazków z
d:/Game Work/special/water/jest to sekwencja (animacja pływów wody)

Następnie cała tafla wody randomowo zmienia swoją wysokość, co naprawdę nieestetycznie wygląda

Chciałbym aby była możliwość renderowania realistycznej wody. Tylko wiecie, jestem jeszcze bardzo zielony.

Bardzo proszę was, bardziej doświadczonych już ludzi o pomoc.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Zielony

  • Użytkownik
    • Ghurund Engine

  • +1
# Lipiec 01, 2014, 23:18:35
Musisz sobie odpowiedzieć na kilka pytań:

1. Jaki zbiornik rysujesz:
 - dla większych są modele statystyczne, animowany szum sprawdza się nieźle,
 - dla mniejszych można policzyć pełną animację z wrzucaniem rzeczy do wody,
 - dla rzek można zrobić animację ruchu cząsteczek zgodnie z prądem

2. Jakie chcesz mieć zachowanie tafli wody:
 - realistyczne odpowiedzi można uzyskać licząc równania naprężeń (Naviera-Stokesa etc.)
 - niezłe wyniki dostaje się z integracji Verleta
 - można tylko dodawać predefiniowane animacje fali kolistej w miejscach zetknięcia przedmiotu z taflą

3. Jakie efekty ośrodka Cię interesują:
 - odbicie (reflection),
 - ugięcie (refraction),
 - promienie światła widoczne spod wody,
 - cienie na powierzchni, na dnie, albo wolumetryczne
 - mgła ze względu na zanieczyszczenie

Trochę informacji jest nawet na Warsztacie:
 - http://warsztat.gd/wiki/Rendering+realistycznej+wody+jako+efekt+post-process
 - http://polygon.hosting.polibuda.info/Prez/Marcin%20Korniluk%20-%20Modelowanie%20powierzchni%20cieczy.pdf

Offline Krzysiek K.

  • Moderator
    • DevKK.net

# Lipiec 01, 2014, 23:48:11
Cytuj
Musisz sobie odpowiedzieć na kilka pytań
A żeby na te pytania móc w ogóle odpowiedzieć, proponuję najpierw poszperać i pozbierać materiały referencyjne - obrazki czy animacje przedstawiające wodę najbliższą takiej, jaką byś chciał ją u siebie widzieć.

Offline Zyper

  • Użytkownik

# Lipiec 02, 2014, 00:05:15
A source code Metina został tak publicznie udostępniony? (bo to Metin prawda?)

Rozwiązanie zależy od tego co chcesz uzyskać. Proponuję poduczenie się w tej dziedzinie i zadanie konkretniejszego pytania, bo "realistyczna woda" nie zbyt jasne.

Offline baran860

  • Użytkownik

# Lipiec 02, 2014, 00:55:36
A source code Metina został tak publicznie udostępniony? (bo to Metin prawda?)

Rozwiązanie zależy od tego co chcesz uzyskać. Proponuję poduczenie się w tej dziedzinie i zadanie konkretniejszego pytania, bo "realistyczna woda" nie zbyt jasne.
Tak to do Metina, chciałbym uzyskać efekt zbliżony do taki jak w silniku graficznym Barok Engine
https://www.youtube.com/watch?v=JwOxpDpVj4E
No, może "lekko" przesadziłem :D

Musisz sobie odpowiedzieć na kilka pytań:

1. Jaki zbiornik rysujesz:
 - dla większych są modele statystyczne, animowany szum sprawdza się nieźle,
 - dla mniejszych można policzyć pełną animację z wrzucaniem rzeczy do wody,
 - dla rzek można zrobić animację ruchu cząsteczek zgodnie z prądem

2. Jakie chcesz mieć zachowanie tafli wody:
 - realistyczne odpowiedzi można uzyskać licząc równania naprężeń (Naviera-Stokesa etc.)
 - niezłe wyniki dostaje się z integracji Verleta
 - można tylko dodawać predefiniowane animacje fali kolistej w miejscach zetknięcia przedmiotu z taflą

3. Jakie efekty ośrodka Cię interesują:
 - odbicie (reflection),
 - ugięcie (refraction),
 - promienie światła widoczne spod wody,
 - cienie na powierzchni, na dnie, albo wolumetryczne
 - mgła ze względu na zanieczyszczenie

Trochę informacji jest nawet na Warsztacie:
 - http://warsztat.gd/wiki/Rendering+realistycznej+wody+jako+efekt+post-process
 - http://polygon.hosting.polibuda.info/Prez/Marcin%20Korniluk%20-%20Modelowanie%20powierzchni%20cieczy.pdf


1. Raczej dla większych obszarów, rzek będzie nieco mniej.
2.
3.Chyba odbicie :D

4.Zaraz zagłębię się bardziej w podesłane mi tutaj linki, za dotychczasową pomoc serdecznie dziękuję.


Jak tylko coś uda mi się napisać/poprawić zgłoszę się do was.

Offline baran860

  • Użytkownik

# Lipiec 02, 2014, 13:42:09
Sorry za post pod postem ale nie radzę sobie z tym, cały czas deformuje mi się cała tafla wody czyli albo przemieszcza mi się góra dół albo lewo prawo.

Ale ciągle cała, nie mam pojęcia jak uzyskać choćby namiastkę tej powierzchni wody.

Offline kolarz3

  • Użytkownik

# Lipiec 02, 2014, 16:50:01
Siema, dostałem w swoje ręce source code pewnej gry i postanowiłem sobie ją "udoskonalić" lecz zatrzymałem się na renderingu wody. Część odpowiadająca za jej "renderowanie" wygląda tak:

#include "StdAfx.h"
#include "../eterLib/StateManager.h"
#include "../eterLib/ResourceManager.h"

#include "MapOutdoor.h"
#include "TerrainPatch.h"

void CMapOutdoor::LoadWaterTexture()
{
UnloadWaterTexture();
char buf[256];
for (int i = 0; i < 30; ++i)
{
sprintf(buf, "d:/Game Work/special/water/%02d.dds", i+1);
m_WaterInstances[i].SetImagePointer((CGraphicImage *) CResourceManager::Instance().GetResourcePointer(buf));
}
}

void CMapOutdoor::UnloadWaterTexture()
{
for (int i = 0; i < 30; ++i)
m_WaterInstances[i].Destroy();
}

void CMapOutdoor::RenderWater()
{
if (m_PatchVector.empty())
return;

if (!IsVisiblePart(PART_WATER))
return;

//////////////////////////////////////////////////////////////////////////
// RenderState
D3DXMATRIX matTexTransformWater;

STATEMANAGER.SaveRenderState(D3DRS_ZWRITEENABLE, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
STATEMANAGER.SaveRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
STATEMANAGER.SaveRenderState(D3DRS_COLORVERTEX, TRUE);

STATEMANAGER.SetTexture(0, m_WaterInstances[((ELTimer_GetMSec() / 70) % 30)].GetTexturePointer()->GetD3DTexture());

D3DXMatrixScaling(&matTexTransformWater, m_fWaterTexCoordBase, -m_fWaterTexCoordBase, 0.0f);
D3DXMatrixMultiply(&matTexTransformWater, &m_matViewInverse, &matTexTransformWater);

STATEMANAGER.SaveTransform(D3DTS_TEXTURE0, &matTexTransformWater);
STATEMANAGER.SaveVertexShader(D3DFVF_XYZ|D3DFVF_DIFFUSE);

STATEMANAGER.SaveTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);

STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);


STATEMANAGER.SetTexture(1,NULL);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

// RenderState
//////////////////////////////////////////////////////////////////////////

// ą° Ŕ§ ľĆ·ˇ ľÖ´Ď˝ĂĹ°±â...
static float s_fWaterHeightCurrent = 0;
static float s_fWaterHeightBegin = 0;
static float s_fWaterHeightEnd = 0;
static DWORD s_dwLastHeightChangeTime = CTimer::Instance().GetCurrentMillisecond();
static DWORD s_dwBlendtime = 300;

// 1.5ĂĘ ¸¶´Ů şŻ°ć
if ((CTimer::Instance().GetCurrentMillisecond() - s_dwLastHeightChangeTime) > s_dwBlendtime)
{
s_dwBlendtime = random_range(1000, 3000);

if (s_fWaterHeightEnd == 0)
s_fWaterHeightEnd = -random_range(0, 15);
else
s_fWaterHeightEnd = 0;

s_fWaterHeightBegin = s_fWaterHeightCurrent;
s_dwLastHeightChangeTime = CTimer::Instance().GetCurrentMillisecond();
}

s_fWaterHeightCurrent = s_fWaterHeightBegin + (s_fWaterHeightEnd - s_fWaterHeightBegin) * (float) ((CTimer::Instance().GetCurrentMillisecond() - s_dwLastHeightChangeTime) / (float) s_dwBlendtime);
m_matWorldForCommonUse._43 = s_fWaterHeightCurrent;

m_matWorldForCommonUse._41 = 0.0f;
m_matWorldForCommonUse._42 = 0.0f;
STATEMANAGER.SetTransform(D3DTS_WORLD, &m_matWorldForCommonUse);

float fFogDistance = __GetFogDistance();

std::vector<std::pair<float, long> >::iterator i;

for(i = m_PatchVector.begin();i != m_PatchVector.end(); ++i)
{
if (i->first<fFogDistance)
DrawWater(i->second);
}

STATEMANAGER.SetTexture(0, NULL);
STATEMANAGER.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);

for(i = m_PatchVector.begin();i != m_PatchVector.end(); ++i)
{
if (i->first>=fFogDistance)
DrawWater(i->second);
}

// ·»´ő¸µ ÇŃ ČÄżˇ´Â ą° z Ŕ§Äˇ¸¦ şą±¸
m_matWorldForCommonUse._43 = 0.0f;

//////////////////////////////////////////////////////////////////////////
// RenderState
STATEMANAGER.RestoreVertexShader();
STATEMANAGER.RestoreTransform(D3DTS_TEXTURE0);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_MINFILTER);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_MAGFILTER);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_MIPFILTER);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSU);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSV);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_TEXCOORDINDEX);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS);

STATEMANAGER.RestoreRenderState(D3DRS_DIFFUSEMATERIALSOURCE);
STATEMANAGER.RestoreRenderState(D3DRS_COLORVERTEX);
STATEMANAGER.RestoreRenderState(D3DRS_ZWRITEENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHABLENDENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE);
}

void CMapOutdoor::DrawWater(long patchnum)
{
assert(NULL!=m_pTerrainPatchProxyList);
if (!m_pTerrainPatchProxyList)
return;

CTerrainPatchProxy& rkTerrainPatchProxy = m_pTerrainPatchProxyList[patchnum];

if (!rkTerrainPatchProxy.isUsed())
return;

if (!rkTerrainPatchProxy.isWaterExists())
return;

CGraphicVertexBuffer* pkVB=rkTerrainPatchProxy.GetWaterVertexBufferPointer();
if (!pkVB)
return;

if (!pkVB->GetD3DVertexBuffer())
return;

UINT uPriCount=rkTerrainPatchProxy.GetWaterFaceCount();
if (!uPriCount)
return;

STATEMANAGER.SetStreamSource(0, pkVB->GetD3DVertexBuffer(), sizeof(SWaterVertex));
STATEMANAGER.DrawPrimitive(D3DPT_TRIANGLELIST, 0, uPriCount);

ms_faceCount += uPriCount;
}

Może lekko objaśnie działanie:
na samym początku jest wczytywane 30 obrazków z
d:/Game Work/special/water/jest to sekwencja (animacja pływów wody)

Następnie cała tafla wody randomowo zmienia swoją wysokość, co naprawdę nieestetycznie wygląda

Chciałbym aby była możliwość renderowania realistycznej wody. Tylko wiecie, jestem jeszcze bardzo zielony.

Bardzo proszę was, bardziej doświadczonych już ludzi o pomoc.
Zajrzyj sobie do dema Nvidii Island: https://www.youtube.com/watch?v=rIKez1QvDcc&hd=1
W necie na pewno można pobrać kod tej aplikacji. Oni renderują ją z wykorzystaniem tesalacji, jest też zaprogramowana refleksje wody.
W książce Jasona Zinka: http://www.amazon.com/Practical-Rendering-Computation-Direct3D-11/dp/1568817207 w ostatnim dziale jest szczegółowo opisana metoda renderingu wody.
Tutaj jedna kartka :

Offline _OskaR

  • Użytkownik

# Lipiec 02, 2014, 17:08:27
cały czas deformuje mi się cała tafla wody czyli albo przemieszcza mi się góra dół albo lewo prawo.

Ale ciągle cała, nie mam pojęcia jak uzyskać choćby namiastkę tej powierzchni wody.
Ni screena problemu, ni screena siatki, ni kodu - nie pomagasz.