hi i am learning directx11 recently.
and i have problem in using constant buffer.
so what i am doing is, i create the constant buffer for directional light and update the value, so that i can use this in my shader.
constant buffer structure in application side
struct SHADER_DIRECTIONAL_LIGHT {
XMFLOAT4 ambient;
XMFLOAT4 diffuse;
XMFLOAT4 specular;
XMFLOAT4 dir;
XMFLOAT4 enabled;
XMFLOAT4 intensity;
};
class DirectionalLight
{
private:
//1. member variable
SHADER_DIRECTIONAL_LIGHT m_data;
//2. static variable
//static SHADER_DIRECTIONAL_LIGHT m_data;
//3. allocated variable
//SHADER_DIRECTIONAL_LIGHT* m_data;
}
constant buffer structure in shader side
cbuffer DIRECTIONAL_LIGHT : register(b0)
{
float4 d_Ambient;
float4 d_Diffuse;
float4 d_Specular;
float4 d_Dir;
float4 d_Enabled;
float4 d_intensity;
};
how i update constant buffer
//of course, edit `m_data` before using map
D3D11_MAPPED_SUBRESOURCE mappedData;
ZeroMemory(&mappedData, sizeof(D3D11_MAPPED_SUBRESOURCE));
HRESULT hr = dContext->Map(m_cb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData);
r_assert(hr);
CopyMemory(mappedData.pData, &m_data, sizeof(SHADER_DIRECTIONAL_LIGHT));
dContext->Unmap(m_cb, 0);
dContext->PSSetConstantBuffers(SHADER_REG_CB_DIRECTIONAL_LIGHT, 1, &m_cb);
and the problem is that
when creating m_data like SHADER_DIRECTIONAL_LIGHT m_data; or static SHADER_DIRECTIONAL_LIGHT m_data; ,
it works fine and the value i updated into the constant buffer using map properly applied into shader side too.
But when i create m_data like SHADER_DIRECTIONAL_LIGHT* m_data, the value i update doesn't really work. the value in shader side is just uninitialized random value.
by the debug, i am just guessing the problem is from the different memory space of variable that i use to update the constant buffer. if i use variable in stack, i successfully update the constant bufferm, and when using variable from heap, it doesn't.
hope somebody to clarify what actually problem here.
thanks
You are passing address of a member pointer m_data with the call
CopyMemory(mappedData.pData, &m_data, sizeof(SHADER_DIRECTIONAL_LIGHT));
Since pointer already holds the address, pass it without address operator like this:
CopyMemory(mappedData.pData, m_data, sizeof(SHADER_DIRECTIONAL_LIGHT));
Related
I am studying with raywenderlich metal by tutorials book. there is an example in the book about selection object. I am new in Metal and I am sorry about my English.
typedef struct {
uint width;
uint height;
uint tiling;
uint lightCount;
vector_float3 cameraPosition;
uint objectId;
uint touchX;
uint touchY;
} Params;
//this struct sent to fragment shader by
encoder.setFragmentBytes(¶ms, length: MemoryLayout<Params>.stride, index:
ParamsBuffer.index)
//end hit test in fragment shader like this
fragment float4 fragment_main(constant Params ¶ms [[buffer(ParamsBuffer)]],
//...
if(params.objectId != 0 && objectID == params.objectId) {
material.baseColor = float3(0.9, 0.5, 0);
}
just I can hit test in fragment shader.. if I want get selected objectId value in swift side
what should I do.. or suppose to be hit test just here.. I didn't understand this part. values can be share CPU and GPU side (in this example objectId) or just I have to process values in shader which value I sent. Is somebody can explain me please?
selection test working correctly but..
objectId value always 0 when I try get objectId value in swift side
print(params.objectId)
Is there a way read constant value from fragment shader in cpu side?
Yes, you have to create a new MTLBuffer object from the specified data and then binding it to the fragment shader, with the setFragmentBuffer(_:offset:index:) method.
In a Unity shaderlab shader you can expose shader properties to the material inspector in the editor. This can be done by placing the properties you want to expose in the Properties section like so
Properties
{
_SomeFloat("A Float", float) = 5
}
Unity defines a list of properties in the documentation here.
However this does not include any form of float2 or vector2, just single Float or Vector which consists of xyzw.
I tried setting the property type to float2 And Vector2
_SomeFloat("A Float", float2) = (5,5)
_SomeFloat2("A Float2", Vector2) = (5,5)
which both return the error Parse error: syntax error, unexpected TVAL_ID at line 7
or trying to cut down the Vector in half by setting only half the members
_SomeFloat("A Float", Vector) = (5,5)
which return the error Parse error: syntax error, unexpected ')', expecting ','
I could just use the Vector type and only use its xy, but that makes for unclear UI as there are now two unused elements in the inspector, and could not find a Property Attribute or Drawer (Such as HideInInspector) that allows you to hide the zw values from the inspector.
So is there a way to expose a float2 using a property type? Or maybe an alternative where you can place two float properties next to each other in the editor like Tiling/Offset drawer is in the standard 2D property type (Maybe something similar to [EditorGUILayout.BeginHorizontal][2])?
From quick search I've found there's MaterialPropertyDrawer that can be extended to add custom tags in shader inspectors (ref: https://docs.unity3d.com/ScriptReference/MaterialPropertyDrawer.html).
Thus, you could use Vector property in shader, create custom attribute, let's say, [ShowAsVector2] and make MaterialPropertyDrawer for it, which would only show two input fields, and assign their value to vector's x and y values. This would result in shader property written as:
[ShowAsVector2] _Position2D("Position", Vector) = (0, 0, 0, 0)
This is an extension to #tsvedas's answer.
using UnityEngine;
using UnityEditor;
/// <summary>
/// Draws a vector2 field for vector properties.
/// Usage: [ShowAsVector2] _Vector2("Vector 2", Vector) = (0,0,0,0)
/// </summary>
public class ShowAsVector2Drawer : MaterialPropertyDrawer
{
public override void OnGUI(Rect position, MaterialProperty prop, GUIContent label, MaterialEditor editor)
{
if( prop.type == MaterialProperty.PropType.Vector )
{
EditorGUIUtility.labelWidth = 0f;
EditorGUIUtility.fieldWidth = 0f;
if (!EditorGUIUtility.wideMode)
{
EditorGUIUtility.wideMode = true;
EditorGUIUtility.labelWidth = EditorGUIUtility.currentViewWidth - 212;
}
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = prop.hasMixedValue;
Vector4 vec = EditorGUI.Vector2Field(position, label, prop.vectorValue);
if (EditorGUI.EndChangeCheck()) {
prop.vectorValue = vec;
}
}
else
editor.DefaultShaderProperty( prop, label.text );
}
}
Simply put this script in an Editor folder, and you should be able to only see the x and y coordinates.
I'm looking at SceneKit's handle binding method with the SCNBufferBindingBlock call back as described here:
https://developer.apple.com/documentation/scenekit/scnbufferbindingblock
Does anyone have an example of how this works?
let program = SCNProgram()
program.handleBinding(ofBufferNamed: "", frequency: .perFrame) { (steam, theNode, theShadable, theRenderer) in
}
To me it reads like I can use a *.metal shader on a SCNNode without having to go through the hassle of SCNTechniques....any takers?
Just posting this in case someone else came here looking for a concise example. Here's how SCNProgram's handleBinding() method can be used with Metal:
First define a data structure in your .metal shader file:
struct MyShaderUniforms {
float myFloatParam;
float2 myFloat2Param;
};
Then pass this as an argument to a shader function:
fragment half4 myFragmentFunction(MyVertex vertexIn [[stage_in]],
constant MyShaderUniforms& shaderUniforms [[buffer(0)]]) {
...
}
Next, define the same data structure in your Swift file:
struct MyShaderUniforms {
var myFloatParam: Float = 1.0
var myFloat2Param = simd_float2()
}
Now create an instance of this data structure, changes its values and define the SCNBufferBindingBlock:
var myUniforms = MyShaderUniforms()
myUniforms.myFloatParam = 3.0
...
program.handleBinding(ofBufferNamed: "shaderUniforms", frequency: .perFrame) { (bufferStream, node, shadable, renderer) in
bufferStream.writeBytes(&myUniforms, count: MemoryLayout<MyShaderUniforms>.stride)
}
Here, the string passed to ofBufferNamed: corresponds to the argument name in the fragment function. The block's bufferStream property then contains the user-defined data type MyShaderUniforms which can then be written to with updated values.
The .handleBinding(ofBufferNamed:frequency:handler:) method registers a block for SceneKit to call at render time for binding a Metal buffer to the shader program. This method can only be used with Metal or OpenGL shading language based programs. SCNProgram object helps perform this custom rendering. Program object contains a vertex shader and a fragment shader. Using a program object completely replaces SceneKit’s rendering. Your shaders take input from SceneKit and become responsible for all transform, lighting and shading effects you want to produce. Use .handleBinding() method to associate a block with a Metal shader program to handle setup of a buffer used in that shader.
Here's a link to Developer Documentation on SCNProgram class.
Also you need an instance method writeBytes(_:count:) that copies all your necessary data bytes into the underlying Metal buffer for use by a shader.
SCNTechniqueclass specifically made for post-processing SceneKit's rendering of a scene using additional drawing passes with custom Metal or OpenGL shaders. Using SCNTechnique you can create such effects as color grading or displacement, motion blur and render ambient occlusion as well as other render passes.
Here is a first code's excerpt how to properly use .handleBinding() method:
func useTheseAPIs(shadable: SCNShadable,
bufferStream: SCNBufferStream
voidPtr: UnsafeMutableRawPointer,
bindingBlock: #escaping SCNBindingBlock,
bufferFrequency: SCNBufferFrequency,
bufferBindingBlock: #escaping SCNBufferBindingBlock,
program: SCNProgram) {
bufferStream.writeBytes(voidPtr, count: 4)
shadable.handleBinding!(ofSymbol: "symbol", handler: bindingBlock)
shadable.handleUnbinding!(ofSymbol: "symbol", handler: bindingBlock)
program.handleBinding(ofBufferNamed: "pass",
frequency: bufferFrequency,
handler: bufferBindingBlock)
}
And here is a second code's excerpt:
let program = SCNProgram()
program.delegate = self as? SCNProgramDelegate
program.vertexShader = NextLevelGLContextYUVVertexShader
program.fragmentShader = NextLevelGLContextYUVFragmentShader
program.setSemantic(
SCNGeometrySource.Semantic.vertex.rawValue,
forSymbol: NextLevelGLContextAttributeVertex,
options: nil)
program.setSemantic(
SCNGeometrySource.Semantic.texcoord.rawValue,
forSymbol: NextLevelGLContextAttributeTextureCoord,
options: nil)
if let material = self._material {
material.program = program
material.handleBinding(ofSymbol: NextLevelGLContextUniformTextureSamplerY, handler: {
(programId: UInt32, location: UInt32, node: SCNNode?, renderer: SCNRenderer) in
glUniform1i(GLint(location), 0);
})
material.handleBinding(ofSymbol: NextLevelGLContextUniformTextureSamplerUV, handler: {
(programId: UInt32, location: UInt32, node: SCNNode?, renderer: SCNRenderer) in
glUniform1i(GLint(location), 1);
})
}
Also, look at Simulating refraction in SceneKit
SO post.
What is the connection between:
Using [[stage_in]] in a Metal Shader
Using MTLVertexDescriptor
Using MTKMesh
For example
Is it possible to use [[stage_in]] without using MTLVertexDescriptor?
Is it possible to use MTLVertexDescriptor without using MTKMesh, but an array of a custom struct based data structure? Such as struct Vertex {...}, Array<Vertex>?
Is it possible to use MTKMesh without using MTLVertexDescriptor? For example using the same struct based data structure?
I didn't find this information on the internet, and the Metal Shading Language Specification doesn't even include the words "descriptor" or "mesh".
No. If you try to create a render pipeline state from a pipeline descriptor without a vertex descriptor, and the corresponding vertex function has a [[stage_in]] parameter, the pipeline state creation call will fail.
Yes. After all, when you draw an MTKMesh, you're still obligated to call setVertexBuffer(...) with the buffers wrapped by the mesh's constituent MTKMeshBuffers. You could just as readily create an MTLBuffer yourself and copy your custom vertex structs into it.
Yes. Instead of having a [[stage_in]] parameter, you'd have a parameter attributed with [[buffer(0)]] (assuming all of the vertex data is interleaved in a single vertex buffer) of type MyVertexType *, as well as a [[vertex_id]] parameter that tells you where to index into that buffer.
Here's an example of setting the vertex buffers from an MTKMesh on a render command encoder:
for (index, vertexBuffer) in mesh.vertexBuffers.enumerated() {
commandEncoder.setVertexBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: index)
}
vertexBuffer is of type MTKMeshBuffer, while its buffer property is of type MTLBuffer; I mention this because it can be confusing.
Here is one way in which you might create a vertex descriptor to tell Model I/O and MetalKit to lay out the mesh data you're loading:
let mdlVertexDescriptor = MDLVertexDescriptor()
mdlVertexDescriptor.attributes[0] = MDLVertexAttribute(name: MDLVertexAttributePosition, format: MDLVertexFormat.float3, offset: 0, bufferIndex: 0)
mdlVertexDescriptor.attributes[1] = MDLVertexAttribute(name: MDLVertexAttributeNormal, format: MDLVertexFormat.float3, offset: 12, bufferIndex: 0)
mdlVertexDescriptor.attributes[2] = MDLVertexAttribute(name: MDLVertexAttributeTextureCoordinate, format: MDLVertexFormat.float2, offset: 24, bufferIndex: 0)
mdlVertexDescriptor.layouts[0] = MDLVertexBufferLayout(stride: 32)
You can create a corresponding MTLVertexDescriptor in order to create a render pipeline state suitable for rendering such a mesh:
let vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(mdlVertexDescriptor)!
Here's a vertex struct that matches that layout:
struct VertexIn {
float3 position [[attribute(0)]];
float3 normal [[attribute(1)]];
float2 texCoords [[attribute(2)]];
};
Here's a stub vertex function that consumes one of these vertices:
vertex VertexOut vertex_main(VertexIn in [[stage_in]])
{
}
And finally, here's a vertex struct and vertex function you could use to render the exact same mesh data without a vertex descriptor:
struct VertexIn {
packed_float3 position;
packed_float3 normal;
packed_float2 texCoords;
};
vertex VertexOut vertex_main(device VertexIn *vertices [[buffer(0)]],
uint vid [[vertex_id]])
{
VertexIn in = vertices[vid];
}
Note that in this last case, I need to mark the struct members as packed since by default, Metal's simd types are padded for alignment purposes (specifically, the stride of a float3 is 16 bytes, not 12 as we requested in our vertex descriptor).
I'm in the process of learning opengles / obj-c and creating an app for the iphone that will render multiple 3d models. I've created an object that stores all the important details such as vertices / faces / textures etc but I also want to store the texture name that is currently being used on the model. In my CustomModels.h file I have:
#interface CustomModels : NSObject {
Vertex3D *vertices;
int numberOfFaces;
Face3D *faces;
Tex3D *texCoords;
BOOL active;
NSMutableArray *textures;
GLuint activeTexture;
}
then in my view controller .m file I'm trying to store the texture name like this:
glGenTextures(1, oModel.activeTexture);
But receive this error:
lvalue required as unary '&' operand
I'm a complete starter in obj-c programming so if anyone can point me in the right direction it would be much appreciated! Many thanks!
glGenTextures expects a pointer to a GLuint as its second parameter. You cannot use an Objective-C property (which is just another way of writing [oModel activeTexture]) in this place. Use a temp local variable instead:
GLuint texture = 0;
glGenTextures(1, &texture);
oModel.activeTexture = texture;