0
\$\begingroup\$

I am just trying to send the size of an array from my c# script to my HLSL shader in order to initiliaze a vertex array inside the shader. It just ain't working. As I have read everywhere, we can't use dynamic arrays inside of a shader but I just want to set the array Dimension once, and NOT change it. Is setting the array length as a "const static int" inside the buffer resized by the size of the array that I send through my c# script? Why isn't it working, I have got no clue.

Anyone can help me figure this one out would be of great help.

WHY? I am trying to build terrain chunks using instanced mesh in SharpDX. The instanced mesh is working but i don't just want to send one face of a cube inside the shader, I want to send a whole "top surface" of a cubed chunk inside the shader so that I can THEN modify the vertices of that whole surface. I thought it would just be better performance wise instead of having a shader work on 1 cube face (2 faces), it would work on a whole "TOP surface" of a chunk (10*10 faces).

Thank you, ninekorn

EDIT - here is some code of the Chunk Script. Right here it is without Perlin noise so it just builds a byte array that contains a chunk 10*10*10.:

        int width = 10;
        int height = 10;
        int depth = 10;
        byte[] map;

        public void startBuildingArray(out byte[] mapOfChunk)
        {
            map = new byte[width* height* depth];

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    for (int z = 0; z < depth; z++)
                    {
                        map[x + width * (y + height * z)] = 1;
                    }
                }
            }

            mapOfChunk = map;
         }

Now inside of the script that sends data out to the shader, I use this:

            ShaderDataStruct chunkBufferData = new ShaderDataStruct();
            chunkBufferData.map= mapOfChunk;

            DataStream stream;
            deviceContext.MapSubresource(vertexBuffer, MapMode.WriteDiscard, 
            SharpDX.Direct3D11.MapFlags.None, out stream);
            stream.Position = 0;
            stream.Write(chunkBufferData);
            deviceContext.UnmapSubresource(vertexBuffer, 0);
            stream.Dispose();

The thing is. I don't understand HOW I can even use the chunk inside of the shader since I have to build vertex arrays INSIDE the shader dynamically. HLSL always will give out the error "array dimensions must be literal scalar expressions". I tried with a #define or I tried to put the variable inside the cbuffer to uniform. NOTHING works. It doesn't even matter IF i send the array size through to the shader from c# or not, because IF i dynamically build the chunk vertex INSIDE the shader, I still cannot give the array a custom size that it needs... I'm baffled as to how I can fix this.

EDIT -2019-03-13 - 23h51 East:

Hi, I was able to put a jagged array inside of a shader but there is a problem with doing that. The thing is, I'm not sure I understand HOW to access a SINGLE instanced geometry constantBuffer, if that is even possible. And a single buffer seems to have a capacity of only 4096 bytes which means that i cannot use something bigger than a jagged array that contains 16 primary array length with 16 secondary array length so "int[][] jagArray = new int[16][]" but in the shader I have to explicitly declare the array length so its more like "int[16][16]". Also, I'm not sure I understand how the buffer works in an Instanced Geometry. I mean, The buffer is "sent to all Instanced Geometry Shader" but it's the same data that is sent to all shader. Is there ANY way to send different data to each Instanced Geometry shaders.

My ONLY TWO other options for the moment are

FIRST: to try and use the same texture that contains the chunk data that is sent to ALL instanced geometry shader and accessible in some form with the pixel position and the instance position.

SECOND: Create a number of buffers equal to the number of chunks, then in each shader, put "if" statements that depending on the instance position, to access a certain buffer data instead of another one. This will be insanely long and annoying to code but I am entirely sure that I can code it.

Will post later on the development. But for the moment, here is some code:

Here is the inputElements code:

            InputElement[] inputElements = new InputElement[]
            {
                new InputElement()
                {
                    SemanticName = "POSITION",
                    SemanticIndex = 0,
                    Format = SharpDX.DXGI.Format.R32G32B32_Float,
                    Slot = 0,
                    AlignedByteOffset = 0,
                    Classification = InputClassification.PerVertexData,
                    InstanceDataStepRate = 0
                },
                new InputElement()
                {
                    SemanticName = "TEXCOORD",
                    SemanticIndex = 0,
                    Format = SharpDX.DXGI.Format.R32G32_Float,
                    Slot = 0,
                    AlignedByteOffset = InputElement.AppendAligned,
                    Classification = InputClassification.PerVertexData,
                    InstanceDataStepRate = 0
                },
                new InputElement()
                {
                    SemanticName = "TEXCOORD",
                    SemanticIndex = 1,
                    Format = SharpDX.DXGI.Format.R32G32B32_Float,
                    Slot = 1,
                    AlignedByteOffset = 0,
                    Classification = InputClassification.PerInstanceData,
                    InstanceDataStepRate = 1
                },
                new InputElement()
                {
                    SemanticName = "COLOR",
                    SemanticIndex = 0,
                    Format = SharpDX.DXGI.Format.R32G32B32A32_Float,
                    Slot = 0,
                    AlignedByteOffset = InputElement.AppendAligned,
                    Classification = InputClassification.PerVertexData,
                    InstanceDataStepRate = 0
                },
            };

The constant Buffer code to change the instance position, I just created a basic class that contains only one element, the position in a Vector3:

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < heigth; y++)
                {
                    for (int z = 0; z < depth; z++)
                    {
                        Vector3 position = new Vector3(x, y, z);
                        int xx = x;
                        int yy = y;
                        int zz = z;

                        if (map[x + _width * (y + _height * z)] == 1)
                        {

                            instances[x + _width * (y + _height * z)] = new DInstanceType()
                            {
                                position = position,
                            };
                        }
                    }
                }
            }

            InstanceBuffer = SharpDX.Direct3D11.Buffer.Create(device, BindFlags.VertexBuffer, instances, Utilities.SizeOf<DInstanceType>() * instances.Length, ResourceUsage.Dynamic, CpuAccessFlags.Write, ResourceOptionFlags.None, 0);

And then I update the instanceBuffer with the data which i use as followed:

            DataStream mappedResource;
            deviceContext.MapSubresource(instanceBuffer, MapMode.WriteDiscard, SharpDX.Direct3D11.MapFlags.None, out mappedResource);

            DInstanceType[] instances = new DInstanceType[instanceCount];

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    for (int z = 0; z < depth; z++)
                    {                         
                        Vector3 position = new Vector3(x, y, z);

                        if (byteArray[x + width * (y + height * z)] == 1)
                        {
                            position.X *= 10;
                            position.Y *= 10;
                            position.Z *= 10;
                            instances[x + width * (y + height * z)] = new DInstanceType()
                            {
                                position = position,
                            };
                        }
                    }
                }
            }

            mappedResource.WriteRange(instances,0, instances.Length);
            deviceContext.UnmapSubresource(instanceBuffer, 0);
            mappedResource.Dispose();

And then to put it into the inputassembler.SetvertexBuffers:

            deviceContext.InputAssembler.SetVertexBuffers(0, new[]
            {
                new VertexBufferBinding(vertBuffer, Utilities.SizeOf<SC_VR_Chunk.DVertex>(), 0),
                new VertexBufferBinding(instanceBuffer, Utilities.SizeOf<DInstanceType>(),0),
            });

Here is the Buffer for the Jagged array that I am sending to the shader. And then every instanced geometry shader instance is taking the instance position as their index which makes it SUPER easy to access the jagged array data.

            BufferDescription matrixBufferDescriptionTHREE = new BufferDescription()
            {
                Usage = ResourceUsage.Dynamic,
                SizeInBytes = 4 * 4 * width * height * depth * width * height * depth,
                BindFlags = BindFlags.ConstantBuffer,
                CpuAccessFlags = CpuAccessFlags.Write,
                OptionFlags = ResourceOptionFlags.None,
                StructureByteStride = 0
            };
            bufferForTests = new SharpDX.Direct3D11.Buffer(device, matrixBufferDescriptionTHREE);

I use my own version of a geometry shader with something that I call the 1D for loop and that doesnt make the shader lag. In fact, it is ultra fast and I can loop whatever I want. Here, it's a 3D flattened array that I loop with a 1D loop that uses 3 "if" statements to increment the variables x/y/z so that I can access the data in the main chunk array. It was easy to built that for loop with just testing it in the console and seeing if it was outputting the same thing as 3 for loops x/y/z.

        cbuffer data :register(b0)
        {      
            float4x4 world;
            float4x4 view;
            float4x4 proj;
        }

        cbuffer inData:register(b1)
        {       
            int chunkMap[16][16];
        }

        struct VS_INPUT
        {
            float4 Pos : POSITION;
            float2 Tex : TEXCOORD0;
            float3 instancePos : TEXCOORD1;
            float4 Col : COLOR;
        };

        struct GS_INPUT
        {
            float4 Pos : POSITION;
            float2 Tex : TEXCOORD0;
            float3 instancePos : TEXCOORD1;
            float4 Col : COLOR;
        };

        struct PS_INPUT
        {
            float4 Pos : SV_POSITION;
            float2 Tex : TEXCOORD0;
            float4 Col : COLOR;
        };

        Texture2D diffuseMap;
        SamplerState textureSampler;

        GS_INPUT VS( VS_INPUT input )
        {

            GS_INPUT output = (GS_INPUT)0;
            output.Pos = input.Pos;     
            output.Col = input.Col;
            output.Tex = input.Tex;

            //output.Pos=mul(world, output.Pos)+ float4(input.instancePos,0); // + float4(input.instancePos,0)
            //output.Pos=mul(view, output.Pos);
            //output.Pos=mul(proj, output.Pos);

            output.instancePos = input.instancePos;
            return output;
        }

        float planeSize = 1;

        static int width = 4;
        static int height = 1;
        static int depth = 4;

        static int widther = 4;
        static int heighter = 1;
        static int depther = 4;

        [maxvertexcount(72)]
        void GS( point GS_INPUT input[1], inout TriangleStream<PS_INPUT> TriStream )
        {

            int xMain = input[0].instancePos.x;
            int yMain = input[0].instancePos.y;
            int zMain = input[0].instancePos.z;

            int x = 0;
            int y = 0;
            int z = 0;

            float4 P = float4(input[0].Pos.xyz,1); //+ input[0].instancePos

            float3 forward = float3(0,0,1);
            float3 back = float3(0,0,-1);
            float3 right = float3(1,0,0);
            float3 left = float3(-1,0,0);
            float3 up = float3(0,1,0);
            float3 down = float3(0,-1,0);

            int total = width * height * depth;

            int test[] = chunkMap[xMain + widther * (yMain + heighter * zMain)];

            for(int i = 0;i < total; i++)
            {           
        [branch]if (test[x + width * (y + height * z)] == 1)
                {       
                    //int testY = y + 1;
                    //if (testY >= height)  
                    {           
                        float size = 1; 

                        PS_INPUT vertices[4];

                        for(int i=0;i<1;i++)
                        {                   
                            vertices[i*3+0].Pos=float4(x, y+size, z,1);
                            vertices[i*3+0].Tex=float2(0,1);

                            vertices[i*3+1].Pos=float4(x, y+size, z+size,1);
                            vertices[i*3+1].Tex=float2(1,1);

                            vertices[i*3+2].Pos=float4(x+size, y+size, z,1);
                            vertices[i*3+2].Tex=float2(1,0);

                            vertices[i*3+3].Pos=float4(x+size, y+size, z+size,1);
                            vertices[i*3+3].Tex=float2(0,0);

                            vertices[i*3+0].Pos=mul(world, vertices[i*3+0].Pos);
                            vertices[i*3+1].Pos=mul(world, vertices[i*3+1].Pos);
                            vertices[i*3+2].Pos=mul(world, vertices[i*3+2].Pos);
                            vertices[i*3+3].Pos=mul(world, vertices[i*3+3].Pos);

                            vertices[i*3+0].Pos = mul(view, vertices[i*3+0].Pos + P);
                            vertices[i*3+1].Pos = mul(view, vertices[i*3+1].Pos+ P);
                            vertices[i*3+2].Pos = mul(view, vertices[i*3+2].Pos+ P);
                            vertices[i*3+3].Pos = mul(view, vertices[i*3+3].Pos+ P);

                            vertices[i*3+0].Pos = mul(proj, vertices[i*3+0].Pos);
                            vertices[i*3+1].Pos = mul(proj, vertices[i*3+1].Pos);
                            vertices[i*3+2].Pos = mul(proj, vertices[i*3+2].Pos);   
                            vertices[i*3+3].Pos = mul(proj, vertices[i*3+3].Pos);   

                            vertices[i*3+0].Col= float4(1,0,0,1);
                            vertices[i*3+1].Col= float4(1,0,0,1);
                            vertices[i*3+2].Col= float4(1,0,0,1);
                            vertices[i*3+3].Col= float4(1,0,0,1);

                            TriStream.Append(vertices[i*3+0]);
                            TriStream.Append(vertices[i*3+1]);
                            TriStream.Append(vertices[i*3+2]);

                            TriStream.RestartStrip();   

                            TriStream.Append(vertices[i*3+3]);
                            TriStream.Append(vertices[i*3+2]);
                            TriStream.Append(vertices[i*3+1]);

                            TriStream.RestartStrip();
                        }
                    }
                }

                z++;

                if (z > depth - 1)
                {
                    y++;
                    z = 0;
                }

                if (y > height - 1)
                {
                    x++;
                    y = 0;
                }
                if (x > width - 1)
                {
                    x = 0;
                    //break;
                }
            }
        }

        float4 PS( PS_INPUT input) : SV_Target
        {   
            return diffuseMap.Sample( textureSampler, input.Tex );
        }
\$\endgroup\$
  • \$\begingroup\$ Sounds like you should show us your code where you try to set this value. \$\endgroup\$ – DMGregory Mar 8 at 12:48
  • \$\begingroup\$ Ok. I will provide some code as soon as I am out of work. I really hope that it is possible to do what I want to do. thank you for your reply \$\endgroup\$ – Ninekorn Mar 9 at 21:05
  • \$\begingroup\$ Hi, I put some code inside the question. Hopefully someone can give a little insight as to how they could manipulate chunk data INSIDE of a shader or HOW they can create an array with a newly found number of vertex INSIDE of the shader. One nice thing is I was able to create instances of faces that I can manipulate the position by sending new vertex position of each instances to the shader... Which is awesome... But it forces me to create 1 face per instance instead of a full chunk data. \$\endgroup\$ – Ninekorn Mar 10 at 5:09
  • \$\begingroup\$ In my tests, I can still create 1 million cubes with 6 faces each in Virtual reality (each face containing 2 triangles but each cubes only containing 8 vertex) . Which means that i can have 1 million TOP faces that are next to each other and that are created almost instantly by the shader. which is kinda neat. But I'm always wondering IF sending 1 million chunk that are 10*10*10 for instancing, and THEN modifying the vertex if it would be more performant. Sorry for the spam comments, it wouldnt let me put all of that stuff inside of the same comment. Don't ban me please. \$\endgroup\$ – Ninekorn Mar 10 at 5:09
  • \$\begingroup\$ We don't ban folks for well-meaning comments. If you want to add information to your question though, it's better to use the edit button so it's all in one place, not hidden in a conversation thread. \$\endgroup\$ – DMGregory Mar 10 at 5:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.