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();
}
}
运行效果

浙公网安备 33010602011771号