Materials.axaml代码

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Height="481.6" Width="530.4"
         xmlns:local="using:AvaloniaUI"
        x:Class="AvaloniaUI.Materials"
        Title="Materials">

    <Grid Margin="10" RowDefinitions="auto,*">
        <StackPanel Grid.Row="0" Margin="0,5,0,8" HorizontalAlignment="Center">
            <CheckBox Name="chkBackground" Margin="3" IsThreeState="False" IsChecked="False">Dark Background</CheckBox>
            <CheckBox Name="chkDiffuse" Margin="3" IsThreeState="False" IsChecked="True">DiffuseMaterial</CheckBox>
            <CheckBox Name="chkSpecular" Margin="3" IsThreeState="False" IsChecked="True">SpecularMaterial</CheckBox>
            <CheckBox Name="chkEmissive" Margin="3" IsThreeState="False" IsChecked="False">EmissiveMaterial</CheckBox>
            <CheckBox Name="chkAmbient" Margin="3" IsThreeState="False" IsChecked="True">AmbientMaterial</CheckBox>
            <CheckBox Name="chkTexture" Margin="3" IsThreeState="False" IsChecked="True">ATextureMaterial</CheckBox>
        </StackPanel>

        <Border Grid.Row="1" BorderBrush="Yellow" BorderThickness="1">
            <local:MaterialsView
                Name="view"
                AlbedoUri="avares://AvaloniaUI/Resources/Images/Tree.jpg"
                BackgroundDark="{Binding #chkBackground.IsChecked}"
                UseDiffuse="{Binding #chkDiffuse.IsChecked}"
                UseSpecular="{Binding #chkSpecular.IsChecked}"
                UseEmissive="{Binding #chkEmissive.IsChecked}"
                UseAmbient="{Binding #chkAmbient.IsChecked}"
                UseTexture="{Binding #chkTexture.IsChecked}" />
        </Border>
    </Grid>
</Window>

Materials.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Controls;
using AvaloniaUI.Demos.Book._23.Tools;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using NVector3 = System.Numerics.Vector3;

namespace AvaloniaUI;

public class MaterialsView : OpenGlControlBase
{
    public static readonly StyledProperty<bool> BackgroundDarkProperty =
        AvaloniaProperty.Register<MaterialsView, bool>(nameof(BackgroundDark), true);

    public bool BackgroundDark
    {
        get => GetValue(BackgroundDarkProperty);
        set => SetValue(BackgroundDarkProperty, value);
    }

    public static readonly StyledProperty<bool> UseAmbientProperty =
        AvaloniaProperty.Register<MaterialsView, bool>(nameof(UseAmbient), true);

    public bool UseAmbient
    {
        get => GetValue(UseAmbientProperty);
        set => SetValue(UseAmbientProperty, value);
    }

    public static readonly StyledProperty<bool> UseDiffuseProperty =
        AvaloniaProperty.Register<MaterialsView, bool>(nameof(UseDiffuse), true);

    public bool UseDiffuse
    {
        get => GetValue(UseDiffuseProperty);
        set => SetValue(UseDiffuseProperty, value);
    }

    public static readonly StyledProperty<bool> UseSpecularProperty =
        AvaloniaProperty.Register<MaterialsView, bool>(nameof(UseSpecular), true);

    public bool UseSpecular
    {
        get => GetValue(UseSpecularProperty);
        set => SetValue(UseSpecularProperty, value);
    }

    public static readonly StyledProperty<bool> UseEmissiveProperty =
        AvaloniaProperty.Register<MaterialsView, bool>(nameof(UseEmissive), false);

    public bool UseEmissive
    {
        get => GetValue(UseEmissiveProperty);
        set => SetValue(UseEmissiveProperty, value);
    }

    public static readonly StyledProperty<bool> UseTextureProperty =
        AvaloniaProperty.Register<MaterialsView, bool>(nameof(UseTexture), true);

    public bool UseTexture
    {
        get => GetValue(UseTextureProperty);
        set => SetValue(UseTextureProperty, value);
    }

    public static readonly StyledProperty<Uri?> AlbedoUriProperty =
        AvaloniaProperty.Register<MaterialsView, Uri?>(nameof(AlbedoUri), defaultValue: null);

    public Uri? AlbedoUri
    {
        get => GetValue(AlbedoUriProperty);
        set => SetValue(AlbedoUriProperty, value);
    }

    public readonly AmbientMaterial ambient = new();
    public readonly DiffuseMaterial diffuse = new();
    public readonly SpecularMaterial specular = new();
    public readonly EmissiveMaterial emissive = new();

    private int program;
    private int vao;
    private int vbo;
    private int ebo;
    private int indexCount;

    private readonly Mesh mesh = Mesh.CreateTorus(0.6f, 0.2f, 64, 32);

    private readonly ImageMaterial image = new();
    private Uri? lastAlbedoUri;
    private bool textureDirty = true;

    private float rotX;
    private float rotY;
    private float time;

    private long lastRenderTick;

    private readonly Dictionary<string, int> uniformLocations = [];

    static MaterialsView()
    {
        AlbedoUriProperty.Changed.AddClassHandler<MaterialsView>((v, e) => v.textureDirty = true);
    }

    protected override void OnOpenGlInit(GlInterface gl)
    {
        GL.LoadBindings(new BindingsContext(gl));

        GL.Enable(EnableCap.DepthTest);
        GL.DepthFunc(DepthFunction.Lequal);

        GL.Enable(EnableCap.CullFace);
        GL.CullFace(TriangleFace.Back);

        CreateProgram();
        CreateMeshBuffers(mesh);

        image.flipY = true;
        textureDirty = true;

        lastRenderTick = 0;
    }

    protected override void OnOpenGlDeinit(GlInterface gl)
    {
        image.Dispose();

        if (program != 0) GL.DeleteProgram(program);
        if (ebo != 0) GL.DeleteBuffer(ebo);
        if (vbo != 0) GL.DeleteBuffer(vbo);
        if (vao != 0) GL.DeleteVertexArray(vao);

        program = 0;
        ebo = 0;
        vbo = 0;
        vao = 0;

        uniformLocations.Clear();
        lastRenderTick = 0;
    }

    protected override void OnOpenGlRender(GlInterface gl, int fb)
    {
        RequestNextFrameRendering();

        GL.BindFramebuffer(FramebufferTarget.Framebuffer, fb);

        var width = Math.Max(1, (int)Bounds.Width);
        var height = Math.Max(1, (int)Bounds.Height);

        GL.Viewport(0, 0, width, height);

        EnsureTexture();

        var clear = BackgroundDark ? 0f : 0.85f;
        GL.ClearColor(clear, clear, clear, 1f);
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

        var now = Stopwatch.GetTimestamp();
        if (lastRenderTick == 0)
            lastRenderTick = now;

        var dt = (float)((now - lastRenderTick) / (double)Stopwatch.Frequency);
        lastRenderTick = now;

        if (dt < 0f) dt = 0f;
        if (dt > 0.1f) dt = 0.1f;

        time += dt;
        rotY += 0.6f * dt;
        rotX = 0.3f * MathF.Sin(time * 0.6f);

        var aspect = width / (float)height;

        var model =
            Matrix4.CreateRotationX(rotX) *
            Matrix4.CreateRotationY(rotY);

        var eye = new Vector3(0f, 0.8f, 3.0f);

        var view =
            Matrix4.LookAt(
                eye,
                Vector3.Zero,
                Vector3.UnitY);

        var proj =
            Matrix4.CreatePerspectiveFieldOfView(
                MathHelper.DegreesToRadians(35f),
                aspect,
                0.2f,
                10f);

        var mvp = model * view * proj;

        var normalMat = new Matrix3(model);
        normalMat.Invert();
        normalMat.Transpose();

        GL.UseProgram(program);

        SetMat4("uMvp", mvp);
        SetMat4("uModel", model);
        SetMat3("uNormalMat", normalMat);

        SetVec3("uLightDir", NVector3.Normalize(new NVector3(-0.6f, -0.5f, -0.6f)));
        SetVec3("uViewPos", new NVector3(eye.X, eye.Y, eye.Z));

        SetInt("uUseAmbient", UseAmbient ? 1 : 0);
        SetInt("uUseDiffuse", UseDiffuse ? 1 : 0);
        SetInt("uUseSpecular", UseSpecular ? 1 : 0);
        SetInt("uUseEmissive", UseEmissive ? 1 : 0);

        SetVec3("uAmbientColor", ambient.color);
        SetFloat("uAmbientIntensity", ambient.intensity);

        var texOn = UseTexture && image.HasTexture ? 1 : 0;
        SetInt("uUseTexture", texOn);

        var diffuseColor = texOn != 0 ? new NVector3(1f, 1f, 1f) : diffuse.color;
        SetVec3("uDiffuseColor", diffuseColor);
        SetFloat("uDiffuseIntensity", diffuse.intensity);

        SetVec3("uSpecularColor", specular.color);
        SetFloat("uSpecularIntensity", specular.intensity);
        SetFloat("uSpecularPower", specular.power);

        SetVec3("uEmissiveColor", emissive.color);
        SetFloat("uEmissiveIntensity", emissive.intensity);

        if (texOn != 0)
        {
            image.Bind(TextureUnit.Texture0);
            SetInt("uAlbedoTex", 0);
        }

        GL.BindVertexArray(vao);
        GL.DrawElements(PrimitiveType.Triangles, indexCount, DrawElementsType.UnsignedShort, 0);
        GL.BindVertexArray(0);
    }

    private void EnsureTexture()
    {
        if (!textureDirty && lastAlbedoUri == AlbedoUri)
            return;

        lastAlbedoUri = AlbedoUri;
        textureDirty = false;

        image.source = AlbedoUri;
        image.EnsureTexture();
    }

    private void CreateMeshBuffers(Mesh mesh)
    {
        indexCount = mesh.indexCount;

        vao = GL.GenVertexArray();
        vbo = GL.GenBuffer();
        ebo = GL.GenBuffer();

        GL.BindVertexArray(vao);

        GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
        GL.BufferData(
            BufferTarget.ArrayBuffer,
            mesh.vertices.Length * sizeof(float),
            mesh.vertices,
            BufferUsageHint.StaticDraw);

        GL.BindBuffer(BufferTarget.ElementArrayBuffer, ebo);
        GL.BufferData(
            BufferTarget.ElementArrayBuffer,
            mesh.indices.Length * sizeof(ushort),
            mesh.indices,
            BufferUsageHint.StaticDraw);

        var stride = 8 * sizeof(float);

        GL.EnableVertexAttribArray(0);
        GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, stride, 0);

        GL.EnableVertexAttribArray(1);
        GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, stride, 3 * sizeof(float));

        GL.EnableVertexAttribArray(2);
        GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, stride, 6 * sizeof(float));

        GL.BindVertexArray(0);
    }

    private void CreateProgram()
    {
        var vs = CompileShader(ShaderType.VertexShader, VertexShaderSource);
        var fs = CompileShader(ShaderType.FragmentShader, FragmentShaderSource);

        program = GL.CreateProgram();
        GL.AttachShader(program, vs);
        GL.AttachShader(program, fs);
        GL.LinkProgram(program);

        GL.GetProgram(program, GetProgramParameterName.LinkStatus, out var linked);
        if (linked == 0)
        {
            var log = GL.GetProgramInfoLog(program);
            throw new InvalidOperationException(log);
        }

        GL.DeleteShader(vs);
        GL.DeleteShader(fs);

        CacheUniformLocations();
    }

    private void CacheUniformLocations()
    {
        uniformLocations.Clear();

        var names = new[]
        {
            "uMvp","uModel","uNormalMat",
            "uLightDir","uViewPos",
            "uUseAmbient","uUseDiffuse","uUseSpecular","uUseEmissive",
            "uAmbientColor","uAmbientIntensity",
            "uDiffuseColor","uDiffuseIntensity",
            "uSpecularColor","uSpecularIntensity","uSpecularPower",
            "uEmissiveColor","uEmissiveIntensity",
            "uUseTexture","uAlbedoTex"
        };

        foreach (var name in names)
            uniformLocations[name] = GL.GetUniformLocation(program, name);
    }

    private static int CompileShader(ShaderType type, string source)
    {
        var shader = GL.CreateShader(type);
        GL.ShaderSource(shader, source);
        GL.CompileShader(shader);

        GL.GetShader(shader, ShaderParameter.CompileStatus, out var ok);
        if (ok == 0)
        {
            var log = GL.GetShaderInfoLog(shader);
            throw new InvalidOperationException(log);
        }

        return shader;
    }

    private int Loc(string name)
    {
        if (uniformLocations.TryGetValue(name, out var loc))
            return loc;

        loc = GL.GetUniformLocation(program, name);
        uniformLocations[name] = loc;
        return loc;
    }

    private void SetInt(string name, int v)
    {
        var loc = Loc(name);
        if (loc != -1) GL.Uniform1(loc, v);
    }

    private void SetFloat(string name, float v)
    {
        var loc = Loc(name);
        if (loc != -1) GL.Uniform1(loc, v);
    }

    private void SetVec3(string name, NVector3 v)
    {
        var loc = Loc(name);
        if (loc != -1) GL.Uniform3(loc, v.X, v.Y, v.Z);
    }

    private void SetMat4(string name, Matrix4 m)
    {
        var loc = Loc(name);
        if (loc != -1) GL.UniformMatrix4(loc, false, ref m);
    }

    private void SetMat3(string name, Matrix3 m)
    {
        var loc = Loc(name);
        if (loc != -1) GL.UniformMatrix3(loc, false, ref m);
    }

    private sealed class BindingsContext(GlInterface gl) : IBindingsContext
    {
        public IntPtr GetProcAddress(string procName) => gl.GetProcAddress(procName);
    }

    private const string VertexShaderSource = """
#version 300 es
precision highp float;

layout(location=0) in vec3 aPos;
layout(location=1) in vec3 aNormal;
layout(location=2) in vec2 aUv;

uniform mat4 uMvp;
uniform mat4 uModel;
uniform mat3 uNormalMat;

out vec3 vPosWs;
out vec3 vNormalWs;
out vec2 vUv;

void main()
{
    vec4 posWs = uModel * vec4(aPos, 1.0);
    vPosWs = posWs.xyz;
    vNormalWs = normalize(uNormalMat * aNormal);
    vUv = aUv;

    gl_Position = uMvp * vec4(aPos, 1.0);
}
""";

    private const string FragmentShaderSource = """
#version 300 es
precision mediump float;

in vec3 vPosWs;
in vec3 vNormalWs;
in vec2 vUv;

out vec4 FragColor;

uniform vec3 uLightDir;
uniform vec3 uViewPos;

uniform int uUseAmbient;
uniform int uUseDiffuse;
uniform int uUseSpecular;
uniform int uUseEmissive;

uniform vec3 uAmbientColor;
uniform float uAmbientIntensity;

uniform vec3 uDiffuseColor;
uniform float uDiffuseIntensity;

uniform vec3 uSpecularColor;
uniform float uSpecularIntensity;
uniform float uSpecularPower;

uniform vec3 uEmissiveColor;
uniform float uEmissiveIntensity;

uniform int uUseTexture;
uniform sampler2D uAlbedoTex;

void main()
{
    vec3 N = normalize(vNormalWs);
    vec3 L = normalize(-uLightDir);
    vec3 V = normalize(uViewPos - vPosWs);

    vec3 albedo = vec3(1.0);
    if (uUseTexture != 0)
        albedo = texture(uAlbedoTex, vUv).rgb;

    vec3 color = vec3(0.0);

    if (uUseAmbient != 0)
        color += albedo * uAmbientColor * uAmbientIntensity;

    float ndotl = max(dot(N, L), 0.0);

    if (uUseDiffuse != 0)
        color += albedo * uDiffuseColor * uDiffuseIntensity * ndotl;

    if (uUseSpecular != 0 && ndotl > 0.0)
    {
        vec3 H = normalize(L + V);
        float spec = pow(max(dot(N, H), 0.0), uSpecularPower);
        color += uSpecularColor * uSpecularIntensity * spec;
    }

    if (uUseEmissive != 0)
        color += uEmissiveColor * uEmissiveIntensity;

    FragColor = vec4(color, 1.0);
}
""";
}

public partial class Materials : Window
{
    public Materials()
    {
        InitializeComponent();
    }
}

运行效果

image

 

posted on 2026-08-18 11:37  dalgleish  阅读(6)  评论(0)    收藏  举报