There is Texture2DArray in HLSL, but is there any workaround for an array of Texture3D's in HLSL/DirectX?
There are a few options, in both DirectX11 and DirectX12.
DirectX11 has more limitations.
You can declare an array of textures in HLSL as per :
Texture3D textures[3] : register(t0);
Please note that it is not really an array per se, it's actually equivalent to :
Texture3D texture0 : register(t0);
Texture3D texture1 : register(t1);
Texture3D texture2 : register(t2);
So you still need to bind 3 textures.
Also note that your array size must be set in the shader, you cannot have a varying number.
Also dynamic indexing is not allowed, so this is not valid for example:
int3 myvoxel = //something
int index = 0;
if (voxel.x > 5)
{
index = 1;
}
return textures[index].Load(int4(voxel, 0));
The following will not work either :
cbuffer cbIndex : register(b0)
{
uint index;
}
return textures[index].Load(int4(voxel, 0));
In DirectX12, this is less limited.
You still need to set your 3 texture views in a contiguous location in your descriptor table, but :
You can do dynamic indexing so both of the samples above will work.
You can also declare unbounded arrays, so you as above need to make sure all your volumes are contiguous in the descriptor table.
Texture3D textures[] : register(t0);
Related
I'm trying to make a simple 3D modeling tool.
there is some work to move a vertex( or vertices ) for transform the model.
I used dynamic vertex buffer because thought it needs much update.
but performance is too low in high polygon model even though I change just one vertex.
is there other methods? or did I wrong way?
here is my D3D11_BUFFER_DESC
Usage = D3D11_USAGE_DYNAMIC;
CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
BindFlags = D3D11_BIND_VERTEX_BUFFER;
ByteWidth = sizeof(ST_Vertex) * _nVertexCount
D3D11_SUBRESOURCE_DATA d3dBufferData;
d3dBufferData.pSysMem = pVerticesInfo;
hr = pd3dDevice->CreateBuffer(&descBuffer, &d3dBufferData, &_pVertexBuffer);
and my update funtion
D3D11_MAPPED_SUBRESOURCE d3dMappedResource;
pImmediateContext->Map(_pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3dMappedResource);
ST_Vertex* pBuffer = (ST_Vertex*)d3dMappedResource.pData;
for (int i = 0; i < vIndice.size(); ++i)
{
pBuffer[vIndice[i]].xfPosition.x = pVerticesInfo[vIndice[i]].xfPosition.x;
pBuffer[vIndice[i]].xfPosition.y = pVerticesInfo[vIndice[i]].xfPosition.y;
pBuffer[vIndice[i]].xfPosition.z = pVerticesInfo[vIndice[i]].xfPosition.z;
}
pImmediateContext->Unmap(_pVertexBuffer, 0);
As mentioned in the previous answer, you are updating your whole buffer every time, which will be slow depending on model size.
The solution is indeed to implement partial updates, there are two possibilities for it, you want to update a single vertex, or you want to update
arbitrary indices (for example, you want to move N vertices in one go, in different locations, like vertex 1,20,23 for example.
The first solution is rather simple, first create your buffer with the following description :
Usage = D3D11_USAGE_DEFAULT;
CPUAccessFlags = 0;
BindFlags = D3D11_BIND_VERTEX_BUFFER;
ByteWidth = sizeof(ST_Vertex) * _nVertexCount
D3D11_SUBRESOURCE_DATA d3dBufferData;
d3dBufferData.pSysMem = pVerticesInfo;
hr = pd3dDevice->CreateBuffer(&descBuffer, &d3dBufferData, &_pVertexBuffer);
This makes sure your vertex buffer is gpu visible only.
Next create a second dynamic buffer which has the size of a single vertex (you do not need any bind flags in that case, as it will be used only for copies)
_pCopyVertexBuffer
Usage = D3D11_USAGE_DYNAMIC; //Staging works as well
CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
BindFlags = 0;
ByteWidth = sizeof(ST_Vertex);
D3D11_SUBRESOURCE_DATA d3dBufferData;
d3dBufferData.pSysMem = NULL;
hr = pd3dDevice->CreateBuffer(&descBuffer, &d3dBufferData, &_pCopyVertexBuffer);
when you move a vertex, copy the changed vertex in the copy buffer :
ST_Vertex changedVertex;
D3D11_MAPPED_SUBRESOURCE d3dMappedResource;
pImmediateContext->Map(_pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3dMappedResource);
ST_Vertex* pBuffer = (ST_Vertex*)d3dMappedResource.pData;
pBuffer->xfPosition.x = changedVertex.xfPosition.x;
pBuffer->.xfPosition.y = changedVertex.xfPosition.y;
pBuffer->.xfPosition.z = changedVertex.xfPosition.z;
pImmediateContext->Unmap(_pVertexBuffer, 0);
Since you use D3D11_MAP_WRITE_DISCARD, make sure to write all attributes there (not only position).
Now once you done, you can use ID3D11DeviceContext::CopySubresourceRegion to only copy the modified vertex in the current location :
I assume that vertexID is the index of the modified vertex :
pd3DeviceContext->CopySubresourceRegion(_pVertexBuffer,
0, //must be 0
vertexID * sizeof(ST_Vertex), //location of the vertex in you gpu vertex buffer
0, //must be 0
0, //must be 0
_pCopyVertexBuffer,
0, //must be 0
NULL //in this case we copy the full content of _pCopyVertexBuffer, so we can set to null
);
Now if you want to update a list of vertices, things get more complicated and you have several options :
-First you apply this single vertex technique in a loop, this will work quite well if your changeset is small.
-If your changeset is very big (close to almost full vertex size, you can probably rewrite the whole buffer instead).
-An intermediate technique is to use compute shader to perform the updates (thats the one I normally use as its the most flexible version).
Posting all c++ binding code would be way too long, but here is the concept :
your vertex buffer must have BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_UNORDERED_ACCESS; //this allows to write wioth compute
you need to create an ID3D11UnorderedAccessView for this buffer (so shader can write to it)
you need the following misc flags : D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS //this allows to write as RWByteAddressBuffer
you then create two dynamic structured buffers (I prefer those over byteaddress, but vertex buffer and structured is not allowed in dx11, so for the write one you need raw instead)
first structured buffer has a stride of ST_Vertex (this is your changeset)
second structured buffer has a stride of 4 (uint, these are the indices)
both structured buffers get an arbitrary element count (normally i use 1024 or 2048), so that will be the maximum amount of vertices you can update in a single pass.
both structured buffers you need an ID3D11ShaderResourceView (shader visible, read only)
Then update process is the following :
write modified vertices and locations in structured buffers (using map discard, if you have to copy less its ok)
attach both structured buffers for read
attach ID3D11UnorderedAccessView for write
set your compute shader
call dispatch
detach ID3D11UnorderedAccessView for write (this is VERY important)
This is a sample compute shader code (I assume you vertex is position only, for simplicity)
cbuffer cbUpdateCount : register(b0)
{
uint updateCount;
};
RWByteAddressBuffer RWVertexPositionBuffer : register(u0);
StructuredBuffer<float3> ModifiedVertexBuffer : register(t0);
StructuredBuffer<uint> ModifiedVertexIndicesBuffer : register(t0);
//this is the stride of your vertex buffer, since here we use float3 it is 12 bytes
#define WRITE_STRIDE 12
[numthreads(64, 1, 1)]
void CS( uint3 tid : SV_DispatchThreadID )
{
//make sure you do not go part element count, as here we runs 64 threads at a time
if (tid.x >= updateCount) { return; }
uint readIndex = tid.x;
uint writeIndex = ModifiedVertexIndicesBuffer[readIndex];
float3 vertex = ModifiedVertexBuffer[readIndex];
//byte address buffers do not understand float, asuint is a binary cast.
RWVertexPositionBuffer.Store3(writeIndex * WRITE_STRIDE, asuint(vertex));
}
For the purposes of this question I'm going to assume you already have a mechanism for selecting a vertex from a list of vertices based upon ray casting or some other picking method and a mechanism for creating a displacement vector detailing how the vertex was moved in model space.
The method you have for updating the buffer is sufficient for anything less than a few hundred vertices, but on large scale models it becomes extremely slow. This is because you're updating everything, rather than the individual vertices you modified.
To fix this, you should only update the vertices you have changed, and to do that you need to create a change set.
In concept, a change set is nothing more than a set of changes made to the data - a list of the vertices that need to be updated. Since we already know which vertices were modified (otherwise we couldn't have manipulated them), we can map in the GPU buffer, go to that vertex specifically, and copy just those vertices into the GPU buffer.
In your vertex modification method, record the index of the vertex that was modified by the user:
//Modify the vertex coordinates based on mouse displacement
pVerticesInfo[SelectedVertexIndex].xfPosition.x += DisplacementVector.x;
pVerticesInfo[SelectedVertexIndex].xfPosition.y += DisplacementVector.y;
pVerticesInfo[SelectedVertexIndex].xfPosition.z += DisplacementVector.z;
//Add the changed vertex to the list of changes.
changedVertices.add(SelectedVertexIndex);
//And update the GPU buffer
UpdateD3DBuffer();
In UpdateD3DBuffer(), do the following:
D3D11_MAPPED_SUBRESOURCE d3dMappedResource;
pImmediateContext->Map(_pVertexBuffer, 0, D3D11_MAP_WRITE, 0, &d3dMappedResource);
ST_Vertex* pBuffer = (ST_Vertex*)d3dMappedResource.pData;
for (int i = 0; i < changedVertices.size(); ++i)
{
pBuffer[changedVertices[i]].xfPosition.x = pVerticesInfo[changedVertices[i]].xfPosition.x;
pBuffer[changedVertices[i]].xfPosition.y = pVerticesInfo[changedVertices[i]].xfPosition.y;
pBuffer[changedVertices[i]].xfPosition.z = pVerticesInfo[changedVertices[i]].xfPosition.z;
}
pImmediateContext->Unmap(_pVertexBuffer, 0);
changedVertices.clear();
This has the effect of only updating the vertices that have changed, rather than all vertices in the model.
This also allows for some more complex manipulations. You can select multiple vertices and move them all as a group, select a whole face and move all the connected vertices, or move entire regions of the model relatively easily, assuming your picking method is capable of handling this.
In addition, if you record the change sets with enough information (the affected vertices and the displacement index), you can fairly easily implement an undo function by simply reversing the displacement vector and reapplying the selected change set.
In my iOS app, written in Swift, I generate a Metal buffer with:
vertexBuffer = device.newBufferWithBytes(vertices, length: vertices.count * sizeofValue(vertices[0]), options: nil)
And bind it to my shader program with:
renderCommandEncoder.setVertexBuffer(vertexBuffer, offset: 0, atIndex: 1)
In my shader program, written in Metal shading language, can I access the size of the buffer? I would like to access the next vertex in my buffer to do some differential calculation. Something like:
vertex float4 my_vertex(const device packed_float3* vertices [[buffer(1)]],
unsigned int vid[[vertex_id]]) {
float4 vertex = vertices[vid];
// Need to clamp this to not go beyond buffer,
// but how do I know the max value of vid?
float4 nextVertex = vertices[vid + 1];
float4 tangent = nextVertex - vertex;
// ...
}
Is my only option to pass the number of vertices as a uniform?
As far as I know, no you can't because the vertices points to an address. Just like C++, must have two things to know the count or size of an array:
1) know what data type of the array (float or some struct)
AND
2a) the array count for the data type OR
2b) the total bytes of the array.
So yes, you would need to pass the array count as a uniform.
For texture buffers you can.
You can get the size of a texture buffer from within the shader code.
Texture buffers have a get_width() and get_height() function, which return a uint.
uint get_width() const;
uint get_height() const;
But that probably does not answer OP's question about vertex buffers.
Actually you can. You can use the resulting value for loops or conditionals. You can't use it to initialise objects. (so dynamic arrays fail)
uint tempUint = 0; // some random type
uint uintSize = sizeof(tempUint); // get the size for the type
uint aVectorSize = sizeof(aVector) / uintSize; // divide the buffer by the type.
float dynamicArray[aVectorSize]; // this fails
for (uint counter = 0; counter < aVectorSize; ++ counter) {
// do stuff
};
if (aVectorSize > 10) {
// do more stuff
}
I want to copy BasicEffect's fog method to use in my own shader so I don't have to declare a basiceffect shader and my own. The HLSL code of the basic effect was released with one of the downloadable samples on XNA Creators Club a while ago and I thought the method needed would be found within that HLSL file. However, all I can see is a function being called but no actual definition for that function. The function called is:
ApplyFog(color, pin.PositionWS.w);
Does anybody know where the definition is and if it's freely acceptable. Otherwise any help on how to replicate it's effect would be great.
I downloaded the sample from here.
Thanks.
Edit: Stil having problems. Think it's to do with getting depth:
VertexToPixel InstancedCelShadeVSNmVc(VSInputNmVc VSInput, in VSInstanceVc VSInstance)
{
VertexToPixel Output = (VertexToPixel)0;
Output.Position = mul(mul(mul(mul(VSInput.Position, transpose(VSInstance.World)), xWorld), xView), xProjection);
Output.ViewSpaceZ = -VSInput.Position.z / xCameraClipFar;
Is that right? Camera clip far is passed in as a constant.
Heres an example of how to achieve a similar effect
In your Vertex Shader Function, you pass the viewspace Z position, divided by the distance of your farplane, that gives you a nice 0..1 mapping for your depthvalues.
Than, in your pixelshader, you use the lerp function to blend between your original color value, and the fogcolor, heres some (pseudo)code:
cbuffer Input //Im used to DX10+ remove the cbuffer for DX9
{
float FarPlane;
float4 FogColor;
}
struct VS_Output
{
//...Whatever else you need
float ViewSpaceZ : TEXCOORD0; //or whatever semantic you'd like to use
}
VS_Output VertexShader(/*Your Input Here */)
{
VS_Output output;
//...Transform to viewspace
VS_Output.ViewSpaceZ = -vsPosition.Z / FarPlane;
return output;
}
float4 PixelShader(VS_Output input) : SV_Target0 // or COLOR0 depending on DX version
{
const float FOG_MIN = 0.9;
const float FOG_MAX = 0.99;
//...Calculate Color
return lerp(yourCalculatedColor, FogColor, lerp(FOG_MIN, FOG_MAX, input.ViewSpaceZ));
}
I've written this from the top of my head, hope it helps.
The constants i've chose will give you a pretty "steep" fog, choose a smaller value for FOG_MIN to get a smoother fog.
people.
I have a problem passing a float array to vertex shader (HLSL) through constant buffer. I know that each "float" in the array below gets a 16-byte slot all by itself (space equivalent to float4) due to HLSL packing rule:
// C++ struct
struct ForegroundConstants
{
DirectX::XMMATRIX transform;
float bounceCpp[64];
};
// Vertex shader constant buffer
cbuffer ForegroundConstantBuffer : register(b0)
{
matrix transform;
float bounceHlsl[64];
};
(Unfortunately, the simple solution here does not work, nothing is drawn after I made that change)
While the C++ data gets passed, due to the packing rule they get spaced out such that each "float" in the bounceCpp C++ array gets into a 16-byte space all by itself in bounceHlsl array. This resulted in an warning similar to the following:
ID3D11DeviceContext::DrawIndexed: The size of the Constant Buffer at slot 0 of the Vertex Shader unit is too small (320 bytes provided, 1088 bytes, at least, expected). This is OK, as out-of-bounds reads are defined to return 0. It is also possible the developer knows the missing data will not be used anyway. This is only a problem if the developer actually intended to bind a sufficiently large Constant Buffer for what the shader expects.
The recommendation, as being pointed out here and here, is to rewrite the HLSL constant buffer this way:
cbuffer ForegroundConstantBuffer : register(b0)
{
matrix transform;
float4 bounceHlsl[16]; // equivalent to 64 floats.
};
static float temp[64] = (float[64]) bounceHlsl;
main(pos : POSITION) : SV_POSITION
{
int index = someValueRangeFrom0to63;
float y = temp[index];
// Bla bla bla...
}
But that didn't work (i.e. ID3D11Device1::CreateVertexShader never returns). I'm compiling things against Shader Model 4 Level 9_1, can you spot anything that I have done wrong here?
Thanks in advance! :)
Regards,
Ben
One solution, albeit non optimal, is to just declare your float array as
float4 bounceHlsl[16];
then process the index like
float x = ((float[4])(bounceHlsl[i/4]))[i%4];
where i is the index you require.
Well basically,I'm not quite sure how to properly use the Set and Get Parameter methods in DX to use the .fx files.I mean I can't find a good tutorial anywhere.I even had a book about D3D9 and while I got most of it,I'm still unable to use effect files.What's worse is the DirectX Samples provided by microsoft are packed with some DX Utility classes by microsoft and all sorts of other needless complications and I can't quite get it trough the 2k lines of code.I mean I get the basic idea(load,begin,loop with passes,end),but can anyone please point me out to a good tutorial on some simple example.The main thing I don't understand is how to work with the effect parameters :(
Here is a reference sheet I wrote back when I was first learning how to use HLSL shaders in DirectX9. Perhaps it will be of assistance.
IN THE APPLICATION:
Declare needed variables:
ID3DXEffect* shader;
Load the .fx file:
D3DXCreateEffectFromFile( d3dDevice,
_T("filepath.fx"),
0,
0,
0,
0,
&shader,
0
);
Clean up the effect object (some people use a SAFE_RELEASE macro):
if(shader)
shader->Release();
shader = nullptr;
Use the shader to render something:
void Application::Render()
{
unsigned passes = 0;
shader->Begin(&passes,0);
for(unsigned i=0;i<passes;++i)
{
shader->BeginPass(i);
// Set uniforms
shader->SetMatrix("gWorld",&theMatrix);
shader->CommitChanges(); // Not necessary if SetWhatevers are done OUTSIDE of a BeginPass/EndPass pair.
/* Insert rendering instructions here */
// BEGIN EXAMPLE:
d3dDevice->SetVertexDeclaration(vertexDecl);
d3dDevice->SetStreamSource(0,vertexBuffer,0,sizeof(VERT));
d3dDevice->SetIndices(indexBuffer);
d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,numVerts,0,8);
// END EXAMPLE
shader->EndPass();
}
shader->End();
}
IN THE .FX FILE:
Declare the uniforms (variables you want to set from within the application):
float4x4 gWorld : WORLD;
float4x4 gViewProj : VIEWPROJECTION;
float gTime : TIME;
Texture2D gDiffuseTexture; // requires a matching sampler
sampler gDiffuseSampler = sampler_state // here's the matching sampler
{
Texture = <gDiffuseTexture>;
FILTER = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
Define the vertex shader input structure:
struct VS_INPUT // make this match the vertex structure in Application
{
float3 untransformed_pos : POSITION0;
float3 untransformed_nrm : NORMAL0;
float4 color : COLOR0;
float2 uv_coords : TEXCOORD0;
};
Define the pixel shader input structure (vertex shader output):
struct PS_INPUT
{
float4 transformed_pos : POSITION0;
float4 transformed_nrm : NORMAL0;
float4 color : COLOR0;
float2 uv_coords : TEXCOORD0;
};
Define a vertex shader:
PS_INPUT mainVS (VS_INPUT input)
{
PS_INPUT output;
/* Insert shader instructions here */
return output;
}
Define a pixel shader:
float4 mainPS (PS_INPUT input) : COLOR
{
/* Insert shader instructions here */
return float4(resultColor,1.0f);
}
Define a technique:
technique myTechnique
{
// Here is a quick sample
pass FirstPass
{
vertexShader = compile vs_3_0 mainVS();
pixelShader = compile ps_3_0 mainPS();
// Setting a few of the many D3D renderstates via the effect framework
ShadeMode = FLAT; // flat color interpolation across triangles
FillMode = SOLID; // no wireframes, no point drawing.
CullMode = CCW; // cull any counter-clockwise polygons.
}
}
Can you be a bit more specific about where you're having problems?
The basic idea with the API for Effect parameters is to load your .fx file and then use ID3DXEffect::GetParameterByName() or GetParameterBySemantic() to retrieve a D3DXHANDLE to the parameters you want to modify at runtime. Then in your render loop you can set the values for those parameters using the ID3DXEffect::SetXXX() family of functions (which one you use depends on the type of the parameter you are setting, e.g. Float, Vector, Matrix), passing the D3DXHANDLE you retrieved when you loaded the effect.
The reason you work with D3DXHANDLEs and not directly with parameter name strings is performance - it saves doing lots of string compares in your render loop to look up parameters.
A simple example of how you might use this is defining a texture2D parameter called diffuseTex in your .fx file. When you load the .fx file, use
D3DXHANDLE diffuseTexHandle = effect->GetParameterByName(NULL, "diffuseTex");
and then in your render loop set the appropriate diffuse texture for each model you draw using
LPDIRECT3DTEXTURE9 diffuseTexturePtr = GetMeTheRightTexturePlease();
ID3DXEffect::SetTexture(diffuseTexHandle, diffuseTexturePtr);