DX11 游戏开发笔记 (六) 体积云 水面

时间:2024-03-21 11:57:11

DX11 游戏开发笔记 (六) 体积云 水面DX11 游戏开发笔记 (六) 体积云 水面

 对DX11来说,纹理占了它的半边天空,除了其赋予的色彩外,更由于其易于为数据的载体。

如上图,我们仅仅使用了一个球、一个矩形,一个立方体纹理,一个3D纹理、一个无关紧要的水面颜色纹理。

球形与矩形的创建,本文不打算提及,参考博文:https://blog.csdn.net/BonChoix/article/details/8394916

 

立方体纹理和3d纹理使用方式跟2D纹理很像。

创建c++纹理资源:

ID3D11ShaderResourceView	         *m_texView;

D3DX11CreateShaderResourceViewFromFile(device, TextureLoadFileName, 0, 0, &m_texView, 0);

基本上使用这个函数就能应对大部分问题;

ID3DX11EffectShaderResourceVariable   *m_fxTex;

m_fxTex =effect->GetVariableByName("2DTexture")->AsShaderResource();

HR(m_fxTex->SetResource(m_texView));

对应的shader:

2D纹理:

Texture2D   2DTexture;

3D纹理:

Texture3D   3DTexture;

立方体纹理:

TextureCube CubeTexture;

采样方式:

float2 fpos;
float4 color=2DTexture.Sample(samTriLinear, fpos);

float3 fpos;
float4 color=3DTexture.Sample(samTriLinear, fpos);
float3 xyz=3DTexture.Sample(samTriLinear, fpos).xyz;

float3 fpos;
float4 color=CubeTexture.Sample(samTriLinear, fpos);
float3 rgb=CubeTexture.Sample(samTriLinear, fpos).rgb;

2D纹理从uv(xz)采样,3d纹理从xyz采样,立方体纹理从中心点发出的射线向量(vector.xyz)采样。

首先我们先看天空盒的HLSL:

TextureCube CubeTexture;  //纹理资源

cbuffer  cbPerObject
{
	row_major	float4x4 	g_worldViewProj : WORLDVIEWPROJECTION;
}

RasterizerState DisableCulling
{
	CullMode = NONE;
	//FrontCounterClockwise = true;
};

DepthStencilState LessEqualDSS
{
	DepthFunc = LESS_EQUAL;
};

SamplerState samTriLinear
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = WRAP;
	AddressV = WRAP;
};

struct  VertexIn
{
	float3 Pos:POSITION;
};

struct  VertexOut
{
	float4 PosH          :SV_POSITION;
	float3 Texc           :TEXCOORD;
};

VertexOut VS(VertexIn In)
{
	VertexOut  Out = (VertexOut)0;
	Out.Texc = In.Pos;
	Out.PosH = mul(float4(In.Pos, 1.0f), g_worldViewProj)/*.xyww*/;
	return Out;

}

float4  PS(VertexOut In) :SV_Target
{
	return CubeTexture.Sample(samTriLinear, In.Texc);
}

technique11 Draw
{
	Pass p0
	{
	SetVertexShader(CompileShader(vs_5_0, VS()));
	SetPixelShader(CompileShader(ps_5_0, PS()));
	SetRasterizerState(DisableCulling);
	SetDepthStencilState(LessEqualDSS, 0);
   }

}

两个简单的状态设置,稍微提及一下:

RasterizerState DisableCulling
{
	CullMode = NONE;
	//FrontCounterClockwise = true;
};

DepthStencilState LessEqualDSS
{
	DepthFunc = LESS_EQUAL;
};

CullMode = NONE;  三角形背面的图形也渲染。

FrontCounterClockwise = true;  将三角形背面设为正面。(渲染逆时针的三角形)

DepthFunc = LESS_EQUAL;深度比较函数,(像素组成的片元)我在你前面(less:<),所以我显示。

SetDepthStencilState(LessEqualDSS, 0); 0是模板参考值,我们在这里初始化过:

m_deviceContext->ClearDepthStencilView(m_depthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

因为模板跟深度共属一个32位float的寄存器,所以模板可以看成深度的小弟,实现特效什么的。

 Out.PosH = mul(float4(In.Pos, 1.0f), g_worldViewProj).xyww;

这个也很简单,就是z=w,然后z/w=1,这样就是无穷远。

在(平头截体转化成的)齐次剪裁空间裁剪之后,(硬件会自动完成所有的裁剪工作)

硬件会自动执行透视除法,(xyzw/w)。

将顶点从齐次裁剪空间变换到规范化设备空间。(-1<=x<=1,  -1<=y<=1,   0<=z<=1)

OpenGL:(-1<=x<=1,  -1<=y<=1,  -1<=z<=1)

构成 2D 图像的 2D x、y 坐标就会被变换到后台缓冲区中的一个称为视口的矩形区域内 ,

 然后进行拉伸。

 

 

把C++的相关变量设置好,一个3D环境就出来了:

DX11 游戏开发笔记 (六) 体积云 水面

cube纹理网站:http://www.humus.name/index.php?page=Textures

cube纹理创建:https://blog.csdn.net/chenjinxian_3D/article/details/51866112

体积云:

体积云hlsl跟立方体大部分类似,只不过我们从纹理中取的rgb不再是作为颜色输入,

而是参与一个伪随机算法去生成一个颜色。


Texture3D   g_VolumeTex;

cbuffer  cbPerObject
{
	row_major	float4x4 	g_worldViewProj : WORLDVIEWPROJECTION;
}

cbuffer  cbPerFrame
{
	float           g_ftime : TIME;
}


RasterizerState DisableCulling
{
	CullMode = NONE;
};


SamplerState samTriLinear
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = WRAP;
	AddressV = WRAP;
};


struct  VertexIn
{
	float3 Pos:POSITION;
};

struct  VertexOut
{
	float4 PosH           :SV_POSITION;
	float3 PosW           :POSITION;
};

VertexOut VS(VertexIn In)
{
	VertexOut  Out = (VertexOut)0;
	Out.PosW = In.Pos;
	Out.PosH = mul(float4(In.Pos, 1.0f), g_worldViewProj);
	return Out;

}

float4  PS(VertexOut In) :SV_Target
{
	float4 sky = float4(0.0f, 0.0f, 1.0f, 0.8f);
	float4 cloud = float4(1.0f, 1.0f, 1.0f, 1.0f);

	In.PosW.xy += g_ftime*3.7;
	In.PosW.z += g_ftime*4.8;

	float noisy = g_VolumeTex.Sample(samTriLinear, In.PosW.xyz/100.0).r;
	float lrp = noisy * 3 - 1.0f;
	
	return lerp(cloud, sky, saturate(lrp));
}

technique11 Draw
{
	Pass p0
	{
	SetVertexShader(CompileShader(vs_5_0, VS()));
	SetPixelShader(CompileShader(ps_5_0, PS()));
	SetRasterizerState(DisableCulling);
         }

}

说白了就是按照一个向量(In.Pos)去取3D纹理中的值,本次体积纹理实际上还是灰度纹理,

rgb都是一个灰度值,(灰度纹理可以做alpha通道,地形,镜面高光等,看你心情,)

取到不同的灰度值(0<x<1),然后按这个值在白色和蓝色中插值取样。

 

In.PosW.xy += g_ftime*3.7;

In.PosW.z += g_ftime*4.8;

作用仅仅只在于移动纹理取样点。

g_ftime:从游戏开始计时的时间(就一个自增变量)。

noisy * 3: 这个值可以改变云的密集度。

 

在c++中设置好纹理、时间、球形。云就成形了:

DX11 游戏开发笔记 (六) 体积云 水面

 

 

波动的水面:

 

上面我们回顾了立方体纹理、体积纹理,你会发现,纹理妙处无穷,而我们使用起来也是

得心应手,就简简单单几个步骤。

那么水面波动怎么实现了:很简单,我们在光照那节知道,我们所谓的灯光其实就是简单的

条件限制,光学反射,其中比较重要的就是平面的法向量。无独有偶,我们这次波动也是在法

向量上做文章。

水面颜色大致可分为三种颜色混合: 

1.水本身的颜色;

2.反射周边物体的颜色;

3.水下物体的颜色。

本次并没有考虑3的情况,因为blending混合并不打算在此节讲述。

 

反射基础知识:https://blog.csdn.net/BonChoix/article/details/8582123

 

hlsl:


TextureCube g_CubeTexture;  //纹理资源
Texture2D   ShaderTexture;
Texture3D   g_BumpTexture;

cbuffer  cbPerObject
{
	row_major	float4x4 	g_worldViewProj : WORLDVIEWPROJECTION;
}

cbuffer  cbPerFrame
{
	float3          g_CameraPos:CAMERAPOS;
	float           g_ftime : TIME;
}

SamplerState samTriLinear
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = WRAP;
	AddressV = WRAP;
};


struct  VertexIn
{
	float3 Pos:POSITION;
	float3 Normal:NORMAL;
	float2 Texture:TEXCOORD0;
};

struct  VertexOut
{
	float4 PosH           :SV_POSITION;
	float3 Normal         :NORMAL;
	float2 Texture        :TEXCOORD0;
	float3 Texc           :TEXCOORD1;
};

VertexOut VS(VertexIn In)
{
	VertexOut  Out = (VertexOut)0;
	Out.Texc = In.Pos;
	Out.PosH = mul(float4(In.Pos, 1.0f), g_worldViewProj);
	Out.Normal = In.Normal;
	Out.Texture = In.Texture;
	return Out;

}

float4  PS(VertexOut In) :SV_Target
{
	float4 color = (float4)0;

	float3  fpos = (float3)0;
	fpos.x = g_ftime*0.2 + In.Texture.x*2.3;
	fpos.z = g_ftime*0.1 + In.Texture.y*2.4;
	fpos.y = 0.0f;

	float3 v3BumpVector = g_BumpTexture.Sample(samTriLinear, fpos).xyz;
	float3 v3Bump = 2 * v3BumpVector - 1;
	v3Bump.xy *= 0.135;
	v3Bump.z = 0.3 * abs(v3Bump.z) + 0.2;

    float3 incident = In.Texc - g_CameraPos;
	float3 normal = In.Normal + v3Bump * 0.10f;
	float3 refW = reflect(incident, normal);
	float4 reflectedColor = g_CubeTexture.Sample(samTriLinear, refW);

	float2  Tex = In.Texture + refW.xy*0.1f;
	color = 0.8*reflectedColor + 0.2*ShaderTexture.Sample(samTriLinear, Tex);
	return color;
}

technique11 Draw
{
	Pass p0
	{
	SetVertexShader(CompileShader(vs_5_0, VS()));
	SetPixelShader(CompileShader(ps_5_0, PS()));

}

}

这里我单独讲一下ps的代码:

float4  PS(VertexOut In) :SV_Target
{
	float4 color = (float4)0;

	float3  fpos = (float3)0;
	fpos.x = g_ftime*0.2 + In.Texture.x*2.3;
	fpos.z = g_ftime*0.1 + In.Texture.y*2.4;
	fpos.y = 0.0f;

	float3 v3BumpVector = g_BumpTexture.Sample(samTriLinear, fpos).xyz;
	float3 v3Bump = 2 * v3BumpVector - 1;
	v3Bump.xy *= 0.135;
	v3Bump.z = 0.3 * abs(v3Bump.z) + 0.2;

    float3 incident = In.Texc - g_CameraPos;
	float3 normal = In.Normal + v3Bump * 0.10f;
	float3 refW = reflect(incident, normal);
	float4 reflectedColor = g_CubeTexture.Sample(samTriLinear, refW);

	float2  Tex = In.Texture + refW.xy*0.1f;
	color = 0.8*reflectedColor + 0.2*ShaderTexture.Sample(samTriLinear, Tex);
	return color;
}

我们首先跟制作体积云一样,先从体积纹理(g_BumpTexture)中取得y=0平面

的一个灰度值v3BumpVector (x=y=z),然后对其v3Bump一顿捣鼓(借鉴意义不大)。

去影响normal,改变我们需要的反射向量,

从而改变立方体取值坐标。我还对水的纹理坐标也稍稍做了改动。

最后加颜色累加就行。

 

DX11 游戏开发笔记 (六) 体积云 水面

 

本例水面只是简单的波动,但已经能达到欺骗效果,我们并未改变顶点坐标,

只是简单的像素级扰动,需要注意的是:本例的3d纹理其实使用的数据很少,完全可以

用2d纹理代替,就水面波动而言,我们甚至可以将其做成alpha通道,这也给我们一个实现水面

的方式,就是将水面的波动做成灰度图(精细一点:法线贴图),然后循环读取移动的纹理坐标。

本例还是很粗糙的水面实现,未考虑透明、净蚀、观看角度不同带来的折射与反射的变化、

波动方向、波形、海浪等等。

 

exe:https://pan.baidu.com/s/1D8VCfLGrA_hwa0lMG4tl5w

没有源代码,就是没有源代码。

DX11 游戏开发笔记 (六) 体积云 水面