I'm using SlimDX, targeting DirectX 11 with shader model 4. I have a pixel shader "preProc" which processes my vertices and saves three textures of data. One for per-pixel normals, one for per-pixel position data and one for color and depth (color takes up rgb and depth takes the alpha channel).
I then later use these textures in a postprocessing shader in order to implement Screen Space Ambient Occlusion, however it seems none of the data is getting saved in the first shader.
Here's my pixel shader:
PS_OUT PS( PS_IN input )
{
PS_OUT output;
output.col = float4(0,0,0,0);
output.norm = float4(input.norm,1);
output.pos = input.pos;
return output;
}
which outputs the following struct:
struct PS_OUT
{
float4 col : SV_TARGET0;
float4 norm : SV_TARGET1;
float4 pos : SV_TARGET2;
};
and takes the following struct for input:
struct PS_IN
{
float4 pos : SV_POSITION;
float2 tex : TEXCOORD0;
float3 norm : TEXCOORD1;
};
However in my postprocessing shader:
Texture2D renderTex : register(t1);
Texture2D normalTex : register(t2);
Texture2D positionTex : register(t3);
Texture2D randomTex : register(t4);
SamplerState samLinear : register(s0);
float4 PS(PS_IN input) : SV_Target
{
return float4(getCol(input.tex));
}
It simply outputs a light-blue screen (the colour I reset my render targets to at the start of each frame). getCol has been tested to work and returns a colour from the renderTex material when only dealing with one render target. If I change the pixelshader to instead sample the randomTex texture (which my code previously loaded from a file and is not a render target) everything is rendered fine, so I am confident it is not my post processing shader.
In case it's my slimDX code that's failing here's what I do:
Creating my textures, shaderresourvecviews and rendertargetviews:
Texture2DDescription textureDescription = new Texture2DDescription()
{
Width=texWidth,
Height=texHeight,
MipLevels=1,
ArraySize=3,
Format=SlimDX.DXGI.Format.R32G32B32A32_Float,
SampleDescription = new SlimDX.DXGI.SampleDescription(1,0),
BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
CpuAccessFlags= CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
Usage= ResourceUsage.Default,
};
texture = new Texture2D(device, textureDescription);
renderTargetView = new RenderTargetView[3];
shaderResourceView = new ShaderResourceView[3];
for (int i = 0; i < 3; i++)
{
RenderTargetViewDescription renderTargetViewDescription = new RenderTargetViewDescription()
{
Format = textureDescription.Format,
Dimension = RenderTargetViewDimension.Texture2D,
MipSlice = 0,
};
renderTargetView[i] = new RenderTargetView(device, texture, renderTargetViewDescription);
ShaderResourceViewDescription shaderResourceViewDescription = new ShaderResourceViewDescription()
{
Format = textureDescription.Format,
Dimension = ShaderResourceViewDimension.Texture2D,
MostDetailedMip = 0,
MipLevels = 1
};
shaderResourceView[i] = new ShaderResourceView(device, texture, shaderResourceViewDescription);
}
Rendering to my multiple render targets:
private void renderToTexture(Shader shader)
{
//set the vertex and pixel shaders
context.VertexShader.Set(shader.VertexShader);
context.PixelShader.Set(shader.PixelShader);
//send texture data and a linear sampler to the shader
context.PixelShader.SetShaderResource(texture, 0);
context.PixelShader.SetSampler(samplerState, 0);
//set the input assembler
SetInputAssembler(shader);
//reset the camera's constant buffer
camera.ResetConstantBuffer();
//set the render targets to the textures we will render to
context.OutputMerger.SetTargets(depthStencilView, renderTargetViews);
//clear the render targets and depth stencil
foreach (RenderTargetView view in renderTargetViews)
{
context.ClearRenderTargetView(view, color);
}
context.ClearDepthStencilView(depthStencilView, DepthStencilClearFlags.Depth, 1.0f, 0);
//draw the scene
DrawScene();
}
and then the function when I render my postProcessing shader to the screen:
private void renderTexture(Shader shader)
{
//get a single quad to be the screen we render
Mesh mesh = CreateScreenFace();
//set vertex and pixel shaders
context.VertexShader.Set(shader.VertexShader);
context.PixelShader.Set(shader.PixelShader);
//set the input assembler
SetInputAssembler(shader);
//point the render target to the screen
context.OutputMerger.SetTargets(depthStencil, renderTarget);
//send the rendered textures and a linear sampler to the shader
context.PixelShader.SetShaderResource(renderTargetViews[0], 1);
context.PixelShader.SetShaderResource(renderTargetViews[1], 2);
context.PixelShader.SetShaderResource(renderTargetViews[2], 3);
context.PixelShader.SetShaderResource(random, 4);
context.PixelShader.SetSampler(samplerState, 0);
//clear the render targets and depth stencils
context.ClearRenderTargetView(renderTarget, new Color4(0.52734375f, 0.8046875f, 0.9765625f));
context.ClearDepthStencilView(depthStencil, DepthStencilClearFlags.Depth, 1, 0);
//set the vertex and index buffers from the quad
context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(mesh.VertexBuffer, Marshal.SizeOf(typeof(Vertex)), 0));
context.InputAssembler.SetIndexBuffer(mesh.IndexBuffer, Format.R16_UInt, 0);
//draw the quad
context.DrawIndexed(mesh.indices, 0, 0);
//dispose of the buffers
mesh.VertexBuffer.Dispose();
mesh.IndexBuffer.Dispose();
}
EDIT: I've added the PIX function call output for a single frame of the current run:
Frame 40
//setup
<0x06BDA1D8> ID3D11DeviceContext::ClearRenderTargetView(0x06B66190, 0x0028F068)
<0x06BDA1D8> ID3D11DeviceContext::ClearDepthStencilView(0x06B66138, 1, 1.000f, 0)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0028F010, 0x0028EFF8, 0x0028F00C --> 0x06BF8EE0)
CreateObject(D3D11 Buffer, 0x06BF8EE0)
<0x06BDA1D8> ID3D11DeviceContext::PSSetConstantBuffers(0, 1, 0x0028F084 --> { 0x06BF8EE0 })
<0x0059FF78> ID3D11Device::CreateBuffer(0x0F8DEB58, 0x0F8DEB40, 0x0F8DEB54 --> 0x06BF8F68)
CreateObject(D3D11 Buffer, 0x06BF8F68)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0F70EAD8, 0x0F70EAC0, 0x0F70EAD4 --> 0x06BF8FF0)
CreateObject(D3D11 Buffer, 0x06BF8FF0)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0FAAE9A8, 0x0FAAE990, 0x0FAAE9A4 --> 0x06BF9078)
CreateObject(D3D11 Buffer, 0x06BF9078)
<0x0059FF78> ID3D11Device::GetImmediateContext(0x06BDA1D8 --> 0x5BA8A8D8)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0F8DEB58, 0x0F8DEB40, 0x0F8DEB54 --> 0x06BF9100)
CreateObject(D3D11 Buffer, 0x06BF9100)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0F70EAD8, 0x0F70EAC0, 0x0F70EAD4 --> 0x06BF9188)
CreateObject(D3D11 Buffer, 0x06BF9188)
<0x06BDA1D8> ID3D11DeviceContext::Release()
<0x06BDA1D8> ID3D11DeviceContext::UpdateSubresource(0x06B59270, 0, NULL, 0x06287FA0, 0, 0)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0FAAE9A8, 0x0FAAE990, 0x0FAAE9A4 --> 0x06BF9210)
CreateObject(D3D11 Buffer, 0x06BF9210)
<0x06BDA1D8> ID3D11DeviceContext::VSSetShader(0x06B66298, NULL, 0)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0FC0E978, 0x0FC0E960, 0x0FC0E974 --> 0x06BF9298)
CreateObject(D3D11 Buffer, 0x06BF9298)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0FE8EDE8, 0x0FE8EDD0, 0x0FE8EDE4 --> 0x06BF9320)
CreateObject(D3D11 Buffer, 0x06BF9320)
<0x06BDA1D8> ID3D11DeviceContext::PSSetShader(0x06B666F8, NULL, 0)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0FC0E978, 0x0FC0E960, 0x0FC0E974 --> 0x06BF93A8)
CreateObject(D3D11 Buffer, 0x06BF93A8)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0FE8EDE8, 0x0FE8EDD0, 0x0FE8EDE4 --> 0x06BF9430)
CreateObject(D3D11 Buffer, 0x06BF9430)
<0x0059FF78> ID3D11Device::CreateInputLayout(0x0028EBE0, 3, 0x06286CB8, 152, 0x0028EBD8 --> 0x06BF9D68)
CreateObject(D3D11 Input Layout, 0x06BF9D68)
<0x06BDA1D8> ID3D11DeviceContext::IASetInputLayout(0x06BF9D68)
<0x06BDA1D8> ID3D11DeviceContext::IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
<0x0059FF78> ID3D11Device::GetImmediateContext(0x06BDA1D8 --> 0x5BA8A8D8)
<0x06BDA1D8> ID3D11DeviceContext::Release()
<0x06BDA1D8> ID3D11DeviceContext::VSSetConstantBuffers(0, 1, 0x0028F024 --> { 0x06B59270 })
<0x06BDA1D8> ID3D11DeviceContext::OMSetRenderTargets(3, 0x0028F004 --> { 0x06B65708, 0x06B657B8, 0x06B582E0 }, 0x06B66138)
<0x06BDA1D8> ID3D11DeviceContext::ClearRenderTargetView(0x06B65708, 0x0028EFEC)
<0x06BDA1D8> ID3D11DeviceContext::ClearRenderTargetView(0x06B657B8, 0x0028EFEC)
<0x06BDA1D8> ID3D11DeviceContext::ClearRenderTargetView(0x06B582E0, 0x0028EFEC)
<0x06BDA1D8> ID3D11DeviceContext::ClearDepthStencilView(0x06B66138, 1, 1.000f, 0)
//draw scene for preproc shader (this should output the three render targets)
//DRAW CALLS HIDDEN
<0x0059FF78> ID3D11Device::CreateBuffer(0x0028EE04, 0x0028EDEC, 0x0028EE00 --> 0x06BF94B8)
CreateObject(D3D11 Buffer, 0x06BF94B8)
<0x0059FF78> ID3D11Device::CreateBuffer(0x0028EE04, 0x0028EDEC, 0x0028EE00 --> 0x06BF9540)
CreateObject(D3D11 Buffer, 0x06BF9540)
<0x06BDA1D8> ID3D11DeviceContext::VSSetShader(0x06B66BB8, NULL, 0)
<0x06BDA1D8> ID3D11DeviceContext::PSSetShader(0x06B66E50, NULL, 0)
<0x0059FF78> ID3D11Device::CreateInputLayout(0x0028EB64, 3, 0x05E988E0, 120, 0x0028EB5C --> 0x06BF9E28)
CreateObject(D3D11 Input Layout, 0x06BF9E28)
<0x06BDA1D8> ID3D11DeviceContext::IASetInputLayout(0x06BF9E28)
<0x06BDA1D8> ID3D11DeviceContext::IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
<0x06BDA1D8> ID3D11DeviceContext::OMSetRenderTargets(1, 0x0028EFC0 --> { 0x06B66190 }, 0x06B66138)
<0x06BDA1D8> ID3D11DeviceContext::PSSetShaderResources(1, 3, 0x0028EF3C --> { 0x06B65760, 0x06B58288, 0x06B58338 })
<0x06BDA1D8> ID3D11DeviceContext::PSSetShaderResources(4, 1, 0x0028EFC0 --> { 0x06B66FA0 })
<0x06BDA1D8> ID3D11DeviceContext::ClearRenderTargetView(0x06B66190, 0x0028EFA4)
<0x06BDA1D8> ID3D11DeviceContext::ClearDepthStencilView(0x06B66138, 1, 1.000f, 0)
<0x06BDA1D8> ID3D11DeviceContext::IASetVertexBuffers(0, 1, 0x0028EFAC --> { 0x06BF94B8 }, 0x0028EFB0, 0x0028EFB4)
<0x06BDA1D8> ID3D11DeviceContext::IASetIndexBuffer(0x06BF9540, DXGI_FORMAT_R16_UINT, 0)
//draw quad for post proc shader. This shader takes the three textures in, as well as a random texture, which is added in the second PSSetShaderResources call. The random texture outputs fine.
<0x06BDA1D8> ID3D11DeviceContext::DrawIndexed(6, 0, 0)
<0x06BF94B8> ID3D11Buffer::Release()
<0x06BF9540> ID3D11Buffer::Release()
<0x06B65B00> IDXGISwapChain::Present(0, 0)
EDIT2: I've been doing some reading and perhaps I need to deallocate the textures as render targets after the preProc pass before I pass them in as ShaderResourceViews to my postProcess shader. I assumed calling context.OutputMerger.SetTargets() would deallocate all of the currently assigned render targets and then assign only the render targets specified in the function's parameters. If this isn't the case (I can't yet be sure if it is or isn't), then how would I go about unassigning the render targets in SlimDX?
EDIT3: Ah, according to this MSDN Page, calling OutputMerger.SetRenderTargets() "overrides all bounded render targets and the depth stencil target regardless of the number of render targets in ppRenderTargetViews." so all of my render targets are getting deallocated automatically when I tell the OutputMerger to render to the screen. This leaves me back to square one.
Fixed it by discovering just how silly I am.
When I create my rendertarget I create a Texture2DArray but I'm treating it like an array of Texture2D objects, instead of one object. I have since altered my code to use an array of Texture2D objects and it works very well.
Related
I'm using ARKit with scene reconstruction and need to rendered the captured scene geometry in metal. I can access this geometry through the ARMeshAnchor.geometry, which is a ARMeshGeometry. However when I try rendering it using my custom metal rendering pipeline, nothing renders and I get a bunch of errors like this:
Invalid device load executing vertex function "myVertex" encoder: "0", draw: 3, at offset 4688
Here's a highly simplified version of my code that I've been using for debugging:
struct InOut {
float4 position [[position]];
};
vertex InOut myVertex(
uint vid [[vertex_id]],
const constant float3* vertexArray [[buffer(0)]])
{
TouchInOut out;
const float3 in = vertexArray[vid];
out.position = float4(in.position, 1);
}
fragment float4 myFragment(TouchInOut in [[stage_in]]){
return float4(1, 0, 0, 1);
}
// Setup MTLRenderPipelineDescriptor
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.colorAttachments[0].pixelFormat = .rgba8Unorm
pipelineDescriptor.sampleCount = 1
pipelineDescriptor.vertexFunction = defaultLibrary.makeFunction(name: "myVertex")
pipelineDescriptor.fragmentFunction = defaultLibrary.makeFunction(name: "myFragment")
let vertexDescriptor = MTLVertexDescriptor()
vertexDescriptor.attributes[0].format = .float3
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.layouts[0].stride = MemoryLayout<SIMD3<Float>>.stride
pipelineDescriptor.vertexDescriptor = vertexDescriptor
func render(arMesh: ARMeshAnchor) -> void {
// snip... — Setting up command buffers
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!
renderEncoder.setViewport(MTLViewport(originX: 0, originY: 0, width: 512, height: 512, znear: 0, zfar: 1))
renderEncoder.setRenderPipelineState(renderPipelineState)
let vertices = arMesh.geometry.vertices
let faces = arMesh.geometry.faces
renderEncoder.setVertexBuffer(vertices.buffer, offset: 0, index: 0)
renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: faces.count * 3, indexType: .uint32, indexBuffer: buffer, indexBufferOffset: 0)
renderEncoder.endEncoding()
// snip... — Clean up
}
I can't figure out why this code causes the metal exception. It stops throwing if I cap vid in the shader to around 100, but it still doesn't draw anything properly
What's going on here? Why does my code produce an error and how can I fix it?
The problem here is the alignment/packing of the vertex data.
Each vertex in ARMeshGeometry.vertices consists of 3 float components, for a total size of 12 bytes. The code above assumes that this means the data is a float3 / SIMD3<Float>, however the vertices from ARMeshGeometry are actually tightly packed. So while SIMD3<Float> has a stride of 16, the actual vertex data has a stride of 12.
The larger size of float3 (16) vs the actual size of elements in the vertices buffer (12) results in metal trying to access data off the end of the vertices buffer, producing the error.
There are two important fixes here:
Make sure the MTLVertexDescriptor has the correct stride:
let exampleMeshGeometry: ARMeshGeometry = ...
vertexDescriptor.layouts[0].stride = exampleMeshGeometry.vertices.stride
In the shader, use packed_float3 instead of float3
vertex InOut myVertex(
uint vid [[vertex_id]],
const constant packed_float3* vertexArray [[buffer(0)]])
{
...
}
After fixing these issues, you should be able to properly transfer ARMeshGeometry buffers to your metal shader
I'm drawing 2 different vertex buffers in metal, one with a texture (ignoring vertex color data) and the other without a texture (drawing purely the vertex color data):
let commandBuffer = self.commandQueue.makeCommandBuffer()
let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: rpd)
//render first buffer with texture
commandEncoder.setRenderPipelineState(self.rps)
commandEncoder.setVertexBuffer(self.vertexBuffer1, offset: 0, at: 0)
commandEncoder.setVertexBuffer(self.uniformBuffer, offset: 0, at: 1)
commandEncoder.setFragmentTexture(self.texture, at: 0)
commandEncoder.setFragmentSamplerState(self.samplerState, at: 0)
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: count1, instanceCount: 1)
//render second buffer without texture
commandEncoder.setRenderPipelineState(self.rps)
commandEncoder.setVertexBuffer(self.vertexBuffer2, offset: 0, at: 0)
commandEncoder.setVertexBuffer(self.uniformBuffer, offset: 0, at: 1)
commandEncoder.setFragmentTexture(nil, at: 0)
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: count2, instanceCount: 1)
commandEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
The shader looks like this:
#include <metal_stdlib>
using namespace metal;
struct Vertex {
float4 position [[position]];
float4 color;
float4 texCoord;
};
struct Uniforms {
float4x4 modelMatrix;
};
vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]],
constant Uniforms &uniforms [[buffer(1)]],
uint vid [[vertex_id]])
{
float4x4 matrix = uniforms.modelMatrix;
Vertex in = vertices[vid];
Vertex out;
out.position = matrix * float4(in.position);
out.color = in.color;
out.texCoord = in.texCoord;
return out;
}
fragment float4 fragment_func(Vertex vert [[stage_in]],
texture2d<float> tex2D [[ texture(0) ]],
sampler sampler2D [[ sampler(0) ]]) {
if (vert.color[0] == 0 && vert.color[1] == 0 && vert.color[2] == 0) {
//texture color
return tex2D.sample(sampler2D, float2(vert.texCoord[0],vert.texCoord[1]));
}
else {
//color color
return vert.color;
}
}
Is this there a better way of doing this? Any vertex that i want to use the texture i'm setting to black, and the shader checks to see if the color is black, and if so then use the texture, otherwise use the color.
Also, is there a way to blend the colored polys and textured polys together using a multiply function if they overlap on the screen? It seems like MTLBlendOperation only has options for add/subtract/min/max, no multiply?
Another way to do this would be to have two different fragment functions, one that renders textured fragments and another one that deals with coloured vertices.
First you would need to create two different MTLRenderPipelineState at load time:
let desc = MTLRenderPipelineDescriptor()
/* ...load all other settings in the descriptor... */
// Load the common vertex function.
desc.vertexFunction = library.makeFunction(name: "vertex_func")
// First create the one associated to the textured fragment function.
desc.fragmentFunction = library.makeFunction(name: "fragment_func_textured")
let texturedRPS = try! device.makeRenderPipelineState(descriptor: desc)
// Then modify the descriptor to create the state associated with the untextured fragment function.
desc.fragmentFunction = library.makeFunction(name: "fragment_func_untextured")
let untexturedRPS = try! device.makeRenderPipelineState(descriptor: desc)
Then at render time, before encoding the draw commands of a textured object you set the textured state, and before encoding the draw commands of an untextured object you switch to the untextured one. Like this:
//render first buffer with texture
commandEncoder.setRenderPipelineState(texturedRPS) // Set the textured state
commandEncoder.setVertexBuffer(self.vertexBuffer1, offset: 0, at: 0)
commandEncoder.setVertexBuffer(self.uniformBuffer, offset: 0, at: 1)
commandEncoder.setFragmentTexture(self.texture, at: 0)
commandEncoder.setFragmentSamplerState(self.samplerState, at: 0)
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: count1, instanceCount: 1)
//render second buffer without texture
commandEncoder.setRenderPipelineState(untexturedRPS) // Set the untextured state
commandEncoder.setVertexBuffer(self.vertexBuffer2, offset: 0, at: 0)
commandEncoder.setVertexBuffer(self.uniformBuffer, offset: 0, at: 1)
// No need to set the fragment texture as we don't need it in the fragment function.
// commandEncoder.setFragmentTexture(nil, at: 0)
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: count2, instanceCount: 1)
No change is required for the vertex function. While you need to split the fragment function into two:
fragment float4 fragment_func_textured(Vertex vert [[stage_in]],
texture2d<float> tex2D [[ texture(0) ]],
sampler sampler2D [[ sampler(0) ]]) {
//texture color
return tex2D.sample(sampler2D, float2(vert.texCoord[0],vert.texCoord[1]));
}
fragment float4 fragment_func_untextured(Vertex vert [[stage_in]]) {
//color color
return vert.color;
}
You could even go ahead and have two different vertex functions that output two different vertex structures in order to save a few bytes. In fact the textured fragment function only needs the texCoord field and not the color, while the untextured function is the other way around.
EDIT: You can use this fragment function to use both the texture color and the vertex color:
fragment float4 fragment_func_blended(Vertex vert [[stage_in]],
texture2d<float> tex2D [[ texture(0) ]],
sampler sampler2D [[ sampler(0) ]]) {
// texture color
float4 texture_sample = tex2D.sample(sampler2D, float2(vert.texCoord[0],vert.texCoord[1]));
// vertex color
float4 vertex_sample = vert.color;
// Blend the two together
float4 blended = texture_sample * vertex_sample;
// Or use another blending operation.
// float4 blended = mix(texture_sample, vertex_sample, mix_factor);
// Where mix_factor is in the range 0.0 to 1.0.
return blended;
}
I've been struggling for hours trying to render a simple 2D bitmap in OpenGL ES (iOS). While in OpenGL I could simply use glDrawPixels, it doesn't exist in OpenGL ES, neither does glBegin. Seems like glVertexPointer is now deprecated too.
(Note: the bitmap I'm rendering is constantly changing at 60 FPS, so glDrawPixels is a better solution than using textures)
I failed to find any documented sample code that draws a bitmap using current APIs.
So to put it shortly: given an array of pixels (in RGBX format, for example), how to I render it, potentially scaled using nearest neighbor, using OpenGL ES?
The short answer is to render a textured quad and implement a model matrix to perform various transforms (e.g. scaling).
How to render a textured quad
First you'll need to build a VBO with your quad's vertex positions:
float[] positions = {
+0.5f, +0.5f, +0f, // top right
-0.5f, +0.5f, +0f, // top left
+0.5f, -0.5f, +0f, // bottom right
-0.5f, -0.5f, +0f // bottom left
};
int positionVBO = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
glBufferData(GL_ARRAY_BUFFER, floatBuffer(positions), GL_STATIC_DRAW);
Then pass the necessary info to your vertex shader:
int positionAttribute = glGetAttribLocation(shader, "position");
glEnableVertexAttribArray(positionAttribute);
glVertexAttribPointer(positionAttribute, 3, GL_FLOAT, false, 0, 0);
Now we'll do the same thing but with the quad's texture coordinates:
float[] texcoords = {
1f, 0f, // top right
0f, 0f, // top left
1f, 1f, // bottom right
0f, 1f // bottom left
};
int texcoordVBO = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, texcoordVBO);
glBufferData(GL_ARRAY_BUFFER, floatBuffer(texcoords), GL_STATIC_DRAW);
int textureAttribute = glGetAttribLocation(shader.getId(), "texcoord");
glEnableVertexAttribArray(textureAttribute);
glVertexAttribPointer(textureAttribute, 2, GL_FLOAT, false, 0, 0);
You could interleave this data into a single VBO but I'll leave that to the reader. Regardless we've submitted all the quad vertex data to the GPU and told the shader how to access it.
Next we build our texture buffer assuming we have an object called image:
int texture = glGenTextures();
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.getWidth(), image.getHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, image.getPixels());
And pass that info to the shaders:
int textureUniform = glGetUniformLocation(shader, "image");
glUniform1i(textureUniform, 0);
Check out open.gl's page on Textures for more information.
Finally, the shaders:
vertex.glsl
attribute vec3 position;
attribute vec2 texcoord;
varying vec2 uv;
void main()
{
gl_Position = vec4(position, 1.0);
uv = texcoord;
}
fragment.glsl
varying vec2 uv;
uniform sampler2D image;
void main()
{
gl_FragColor = texture(image, uv);
}
Given no other GL state changes this will render the following:
Note: Since I don't have access to an iOS development environment currently this sample is written in Java. The principle is the same however.
EDIT: How to build the shader program
A shader program is composed from a series of shaders. The bare minimum is a vertex and fragment shader. This is how we would build a shader program from the two shaders above:
String vertexSource = loadShaderSource("vertex.glsl");
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, vertexSource);
glCompileShader(vertexShader);
String fragmentSource = loadFileAsString("fragment.glsl");
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, fragmentSource);
glCompileShader(fragmentShader);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
Once created you would communicate with it via glVertexAttribPointer and glUniform.
I am trying to some GPGPU calculations using HLSL and Direct3D 11.
My goal is to calculate several images sequentially (one rendering per image), then sum them.
To achive this I created three textures: one is an input, two are for summarizing the results. The method I am trying: first I render to texture2 (and I use the texture and texture3 textures as input), then I render to texture3 with a slightly modified pixel shader (and I use the texture and texture2 textures as input). After this, I dump the textures to a PNG file. My problem is that somehow the second pixel shader doesn't get the result of the first shader (texture2), but the original, empty texture. I defined both texture, texture2, and texture3 with usage D3D11_USAGE_DEFAULT, and D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE binding flags.
My HLSL shader (shader1):
float4 main(PS_INPUT input) : SV_TARGET
{
...
float4 val5 = 0.25F * (val + val2 + val3 + val4) + tex3.Sample(sam, input.Tex4);
return val5;
}
The other shader (shader2):
float4 main(PS_INPUT input) : SV_TARGET
{
...
float4 val5 = 0.25F * (val + val2 + val3 + val4) + tex2.Sample(sam, input.Tex4);
return val5;
}
The code that performns the rendering and the dumping of textures:
context->OMSetRenderTargets(1, &targetViewTexture2, NULL);
float z = 280.0F;
GenMatrix(z);
context->VSSetShader(vs, NULL, 0);
context->PSSetShader(ps, NULL, 0);
context->PSSetSamplers(0, 1, &state);
context->VSSetConstantBuffers(0, 1, &cbuf);
context->Draw(6, 0);
swapChain->Present(0, 0);
context->OMSetRenderTargets(1, &targetViewTexture3, NULL);
z = 0.0F;
GenMatrix(z);
context->VSSetShader(vs, NULL, 0);
context->PSSetShader(ps2, NULL, 0);
context->PSSetSamplers(0, 1, &state);
context->VSSetConstantBuffers(0, 1, &cbuf);
context->Draw(6, 0);
swapChain->Present(0, 0);
DirectX::ScratchImage im;
DirectX::CaptureTexture(device, context, texture3, im);
const DirectX::Image *realImage = im.GetImage(0, 0, 0);
HRESULT hr2 = DirectX::SaveToWICFile(*realImage, DirectX::WIC_FLAGS_NONE, GUID_ContainerFormatPng, L"output_tex3.png", NULL);
DirectX::CaptureTexture(device, context, texture2, im);
realImage = im.GetImage(0, 0, 0);
hr2 = DirectX::SaveToWICFile(*realImage, DirectX::WIC_FLAGS_NONE, GUID_ContainerFormatPng, L"output_tex2.png", NULL);
The ouput of the first pass (ps) is correct, but the second is not. If I only leave the shader code:
float4 val5 = tex2.Sample(sam, input.Tex4);
I get an empty image. Am I missing something? Do I have to call some methods to use the texture in the second pixel shader? I think I pinpointed the relevant sections of my large code base, but if you need more information just ask for it in the comment section.
The problem was that I used the texture both as input and output. You could use a texture either as input or render target in one pass.
So I've went through some tutorials and resources such as the DirectX Documentation / Reference itself and either I missed something or I really can't find an answer to my question.
The question is as stated in the title: How does DirectX read in Vertices into Vertex Buffers?
I've understood, of course, that you have to provide one or more FVF codes. But it doesn't say anywhere how to properly set up your Vertex struct. The only thing I could imagine would be, that DirectX checks flags in a linear "timeline", of course, so one flag which may require the same data types and orders must come first in the struct as well.
As a little example for what I mean:
struct MyVertex {
float x, y, z;
float nx, ny, ny;
};
!=
struct MyVertex {
float nx, ny, nz;
float x, y, z;
};
with the FVF codes:
D3DFVF_XYZ | D3DFVF_NORMAL
and nx, ny, nz representing the 3D coordinates of the normal vertex.
Any help on how to properly set up your vertex struct is appreciated...
Sincerely,
Derija
You need to ensure the C++ and HLSL Structure, matches the order in which it was specified in the Vertex Format (if you specified XYZ then Normal, your structure must match this), then you need to use the device->CreateBuffer to create the vertex buffer, from the array of vertex structures, after which the arrays of vertex structures may be released and freed as DirectX will manage the buffer data independently from there, to alter the data in the render loop, the buffer must be writable, and can be updated after create using ID3D10Buffer Map and Unmap.
MSDN:
Create Vertexbufferm http://msdn.microsoft.com/en-us/library/windows/desktop/bb173544(v=vs.85).aspx
Buffer: http://msdn.microsoft.com/en-us/library/windows/desktop/bb173510(v=vs.85).aspx
Eg: C++
D3D10_INPUT_ELEMENT_DESC layoutPosTexNormCInstanced[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0},
{"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0},
{"TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D10_INPUT_PER_VERTEX_DATA, 0},
{"BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 44, D3D10_INPUT_PER_VERTEX_DATA, 0},
{"BLENDINDICES", 0, DXGI_FORMAT_R8G8B8A8_SINT, 0, 56, D3D10_INPUT_PER_VERTEX_DATA, 0 }, //4
{"mTransform", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
{"mTransform", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 16, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
{"mTransform", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 32, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
{"mTransform", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 48, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
};
//Contains the position, texture coordinate and normal for lighting calculations.
struct DX10VertexNormal
{
//Constructor.
DX10VertexNormal()
{
ZeroMemory(this, sizeof(DX10VertexNormal));
boneIndex[0] = -1;
boneIndex[1] = -1;
boneIndex[2] = -1;
boneIndex[3] = -1;
};
//PAD to 4.
D3DXVECTOR3 pos;
D3DXVECTOR2 tcoord;
D3DXVECTOR3 normal;
D3DXVECTOR3 tangent;
D3DXVECTOR3 binormal;
int boneIndex[4];
};
HLSL:
///Holds the vertex shader data for rendering
///instanced mesh data, with position, texture coord,
///and surface normal for lighting calculations.
struct VS_Instanced_PosTexNorm_INPUT
{
float4 Pos: POSITION;
float2 Tex: TEXCOORD;
float3 Norm: NORMAL;
float3 Tangent: TANGENT;
float3 Binormal: BINORMAL;
int4 boneIndex: BLENDINDICES;
row_major float4x4 mTransform : mTransform;
uint InstanceId : SV_InstanceID;
};