Search code examples
shadermetal

Metal shader changing camera z does nothing


I have the following Metal Shader code, if I move the camera X/Y the view updates correctly, however If I move the z position of the camera, it's as if nothing happens, the view renders normally without zooming in/out...I feel like I'm missing something?

Am I creating the transforms and multiplying them correctly?

#include <metal_stdlib>
using namespace metal;

float degreesToRadians(float degrees) {
    return degrees * (M_PI_F / 180.0);
}

// Function to generate a projection matrix
float4x4 generateProjectionMatrix(float fov, float aspectRatio, float near, float far) {
    float yScale = 1.0 / tan(fov * 0.5);
    float xScale = yScale / aspectRatio;
    float zRange = near - far;

    float4x4 projectionMatrix;
    projectionMatrix.columns[0] = float4(xScale, 0.0, 0.0, 0.0);
    projectionMatrix.columns[1] = float4(0.0, yScale, 0.0, 0.0);
    projectionMatrix.columns[2] = float4(0.0, 0.0, (near + far) / zRange, -1.0);
    projectionMatrix.columns[3] = float4(0.0, 0.0, 2.0 * near * far / zRange, 0.0);

    return projectionMatrix;
}

float4x4 generateViewMatrix(float3 cameraPosition) {
    float3 eye = cameraPosition;
    float3 target = float3(0.0, 0.0, 0.0); // Assuming the target is at the origin
    float3 up = float3(0.0, 1.0, 0.0); // Up direction is positive Y-axis

    float3 zAxis = normalize(eye - target);
    float3 xAxis = normalize(cross(up, zAxis));
    float3 yAxis = cross(zAxis, xAxis);

    float4x4 viewMatrix;
    viewMatrix.columns[0] = float4(xAxis.x, yAxis.x, zAxis.x, 0.0);
    viewMatrix.columns[1] = float4(xAxis.y, yAxis.y, zAxis.y, 0.0);
    viewMatrix.columns[2] = float4(xAxis.z, yAxis.z, zAxis.z, 0.0);
    viewMatrix.columns[3] = float4(-dot(xAxis, eye), -dot(yAxis, eye), -dot(zAxis, eye), 1.0);

    return viewMatrix;
}


[[ stitchable ]] float2
accordion(float2 position, float amount, float fov_in) {

    float4 newPos = float4(position.x, position.y, 0.0, 1.0);

    float3 cameraPosition = float3(0.0, 0.0, amount);

    float4x4 viewMatrix = generateViewMatrix(cameraPosition);

    float nearPlane = 0.1;
    float farPlane = 100.0;
    float aspectRatio = 1.0;
    float fov = degreesToRadians(fov_in);

    float4x4 projectionMatrix = generateProjectionMatrix(fov,
                                                       aspectRatio,
                                                       nearPlane,
                                                       farPlane);

    float4 final = projectionMatrix * viewMatrix * newPos;

    return final.xy;
}


Solution

  • You need to enable depth and provide a depth buffer.

    Code from https://metalbyexample.com/modern-metal-2/

    mtkView.colorPixelFormat = .bgra8Unorm_srgb
    mtkView.depthStencilPixelFormat = .depth32Float
    
    pipelineDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat
    pipelineDescriptor.depthAttachmentPixelFormat = view.depthStencilPixelFormat
    
    let depthStencilState: MTLDepthStencilState
    
    static func buildDepthStencilState(device: MTLDevice) -> MTLDepthStencilState {
        let depthStencilDescriptor = MTLDepthStencilDescriptor()
        depthStencilDescriptor.depthCompareFunction = .less
        depthStencilDescriptor.isDepthWriteEnabled = true
        return device.makeDepthStencilState(descriptor: depthStencilDescriptor)!
    }
    
    depthStencilState = Renderer.buildDepthStencilState(device: device)
    
    commandEncoder.setDepthStencilState(depthStencilState)