fyrox-impl 1.0.1

Feature-rich, easy-to-use, 2D/3D game engine with a scene editor. Like Godot, but in Rust.
Documentation
(
    name: "SpotVolumetric",
    resources: [
        (
            name: "depthSampler",
            kind: Texture(kind: Sampler2D, fallback: White),
            binding: 0
        ),
        (
            name: "properties",
            kind: PropertyGroup([
                (name: "worldViewProjection", kind: Matrix4()),
                (name: "invProj", kind: Matrix4()),
                (name: "lightPosition", kind: Vector3()),
                (name: "lightDirection", kind: Vector3()),
                (name: "lightColor", kind: Vector3()),
                (name: "scatterFactor", kind: Vector3()),
                (name: "intensity", kind: Float()),
                (name: "coneAngleCos", kind: Float()),
            ]),
            binding: 0
        ),
    ],
    passes: [
        (
            name: "Primary",

            draw_parameters: DrawParameters(
                cull_face: None,
                color_write: ColorMask(
                    red: true,
                    green: true,
                    blue: true,
                    alpha: true,
                ),
                depth_write: false,
                stencil_test: Some(StencilFunc(
                    func: Equal,
                    ref_value: 0xFF,
                    mask: 0xFFFF_FFFF
                )),
                depth_test: None,
                blend: Some(BlendParameters(
                    func: BlendFunc(
                        sfactor: One,
                        dfactor: One,
                        alpha_sfactor: One,
                        alpha_dfactor: One,
                    ),
                    equation: BlendEquation(
                        rgb: Add,
                        alpha: Add
                    )
                )),
                stencil_op: StencilOp(
                    fail: Keep,
                    zfail: Keep,
                    zpass: Zero,
                    write_mask: 0xFFFF_FFFF,
                ),
                scissor_box: None
            ),

            vertex_shader:
                r#"
                    layout (location = 0) in vec3 vertexPosition;
                    layout (location = 1) in vec2 vertexTexCoord;

                    out vec2 texCoord;

                    void main()
                    {
                        texCoord = vertexTexCoord;
                        gl_Position = properties.worldViewProjection * vec4(vertexPosition, 1.0);
                    }
                "#,

            fragment_shader:
                r#"
                    out vec4 FragColor;

                    in vec2 texCoord;

                    void main()
                    {
                        vec3 fragmentPosition = S_UnProject(vec3(texCoord, texture(depthSampler, texCoord).r), properties.invProj);
                        float fragmentDepth = length(fragmentPosition);
                        vec3 viewDirection = fragmentPosition / fragmentDepth;

                        // Ray-cone intersection
                        float sqrConeAngleCos = properties.coneAngleCos * properties.coneAngleCos;
                        vec3 CO = -properties.lightPosition;
                        float DdotV = dot(viewDirection, properties.lightDirection);
                        float COdotV = dot(CO, properties.lightDirection);
                        float a = DdotV * DdotV - sqrConeAngleCos;
                        float b = 2.0 * (DdotV * COdotV - dot(viewDirection, CO) * sqrConeAngleCos);
                        float c = COdotV * COdotV - dot(CO, CO) * sqrConeAngleCos;

                        // Find intersection
                        vec3 scatter = vec3(0.0);
                        float minDepth, maxDepth;
                        if (S_SolveQuadraticEq(a, b, c, minDepth, maxDepth))
                        {
                            float dt1 = dot((minDepth * viewDirection) - properties.lightPosition, properties.lightDirection);
                            float dt2 = dot((maxDepth * viewDirection) - properties.lightPosition, properties.lightDirection);

                            // Discard points on reflected cylinder and perform depth test.
                            if ((dt1 > 0.0 || dt2 > 0.0) && (minDepth > 0.0 || fragmentDepth > minDepth))
                            {
                                if (dt1 > 0.0 && dt2 < 0.0)
                                {
                                    // Closest point is on cylinder, farthest on reflected.
                                    maxDepth = minDepth;
                                    minDepth = 0.0;
                                }
                                else if (dt1 < 0.0 && dt2 > 0.0)
                                {
                                    // Farthest point is on cylinder, closest on reflected.
                                    minDepth = maxDepth;
                                    maxDepth = fragmentDepth;
                                }

                                minDepth = max(minDepth, 0.0);
                                maxDepth = clamp(maxDepth, 0.0, fragmentDepth);

                                scatter = properties.scatterFactor * S_InScatter(viewDirection * minDepth, viewDirection, properties.lightPosition, maxDepth - minDepth);
                            }
                        }

                        FragColor = vec4(properties.lightColor.xyz * pow(clamp(properties.intensity * scatter, 0.0, 1.0), vec3(2.2)), 1.0);
                    }
                "#,
        )
    ]
)