OpenGlPage.xaml.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Numerics;
  5. using System.Runtime.InteropServices;
  6. using Avalonia;
  7. using Avalonia.Controls;
  8. using Avalonia.OpenGL;
  9. using static Avalonia.OpenGL.GlConsts;
  10. // ReSharper disable StringLiteralTypo
  11. namespace ControlCatalog.Pages
  12. {
  13. public class OpenGlPage : UserControl
  14. {
  15. }
  16. public class OpenGlPageControl : OpenGlControlBase
  17. {
  18. private float _yaw;
  19. public static readonly DirectProperty<OpenGlPageControl, float> YawProperty =
  20. AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("Yaw", o => o.Yaw, (o, v) => o.Yaw = v);
  21. public float Yaw
  22. {
  23. get => _yaw;
  24. set => SetAndRaise(YawProperty, ref _yaw, value);
  25. }
  26. private float _pitch;
  27. public static readonly DirectProperty<OpenGlPageControl, float> PitchProperty =
  28. AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("Pitch", o => o.Pitch, (o, v) => o.Pitch = v);
  29. public float Pitch
  30. {
  31. get => _pitch;
  32. set => SetAndRaise(PitchProperty, ref _pitch, value);
  33. }
  34. private float _roll;
  35. public static readonly DirectProperty<OpenGlPageControl, float> RollProperty =
  36. AvaloniaProperty.RegisterDirect<OpenGlPageControl, float>("Roll", o => o.Roll, (o, v) => o.Roll = v);
  37. public float Roll
  38. {
  39. get => _roll;
  40. set => SetAndRaise(RollProperty, ref _roll, value);
  41. }
  42. static OpenGlPageControl()
  43. {
  44. AffectsRender<OpenGlPageControl>(YawProperty, PitchProperty, RollProperty);
  45. }
  46. private int _vertexShader;
  47. private int _fragmentShader;
  48. private int _shaderProgram;
  49. private int _vertexBufferObject;
  50. private int _indexBufferObject;
  51. private int _vertexArrayObject;
  52. private string WithVersion(string shader) =>
  53. $"#version {(DisplayType == GlDisplayType.OpenGl ? 120 : 100)}\n" + shader;
  54. private string WithVersionAndPrecision(string shader, string precision) =>
  55. WithVersion(DisplayType == GlDisplayType.OpenGLES ? $"precision {precision}\n{shader}" : shader);
  56. private string VertexShaderSource => WithVersion(@"
  57. attribute vec3 aPos;
  58. attribute vec3 aNormal;
  59. uniform mat4 uModel;
  60. uniform mat4 uProjection;
  61. uniform mat4 uView;
  62. varying vec3 FragPos;
  63. varying vec3 VecPos;
  64. varying vec3 Normal;
  65. void main()
  66. {
  67. gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0);
  68. FragPos = vec3(uModel * vec4(aPos, 1.0));
  69. VecPos = aPos;
  70. Normal = normalize(vec3(uModel * vec4(aNormal, 1.0)));
  71. }
  72. ");
  73. private string FragmentShaderSource => WithVersionAndPrecision(@"
  74. varying vec3 FragPos;
  75. varying vec3 VecPos;
  76. varying vec3 Normal;
  77. uniform float uMaxY;
  78. uniform float uMinY;
  79. void main()
  80. {
  81. float y = (VecPos.y - uMinY) / (uMaxY - uMinY);
  82. vec3 objectColor = vec3((1 - y), 0.40 + y / 4, y * 0.75+0.25);
  83. //vec3 objectColor = normalize(FragPos);
  84. float ambientStrength = 0.3;
  85. vec3 lightColor = vec3(1.0, 1.0, 1.0);
  86. vec3 lightPos = vec3(uMaxY * 2, uMaxY * 2, uMaxY * 2);
  87. vec3 ambient = ambientStrength * lightColor;
  88. vec3 norm = normalize(Normal);
  89. vec3 lightDir = normalize(lightPos - FragPos);
  90. float diff = max(dot(norm, lightDir), 0.0);
  91. vec3 diffuse = diff * lightColor;
  92. vec3 result = (ambient + diffuse) * objectColor;
  93. gl_FragColor = vec4(result, 1.0);
  94. }
  95. ", "mediump float");
  96. [StructLayout(LayoutKind.Sequential, Pack = 4)]
  97. private struct Vertex
  98. {
  99. public Vector3 Position;
  100. public Vector3 Normal;
  101. }
  102. private readonly Vertex[] _points;
  103. private readonly ushort[] _indices;
  104. private readonly float _minY;
  105. private readonly float _maxY;
  106. public OpenGlPageControl()
  107. {
  108. var name = typeof(OpenGlPage).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin"));
  109. using (var sr = new BinaryReader(typeof(OpenGlPage).Assembly.GetManifestResourceStream(name)))
  110. {
  111. var buf = new byte[sr.ReadInt32()];
  112. sr.Read(buf, 0, buf.Length);
  113. var points = new float[buf.Length / 4];
  114. Buffer.BlockCopy(buf, 0, points, 0, buf.Length);
  115. buf = new byte[sr.ReadInt32()];
  116. sr.Read(buf, 0, buf.Length);
  117. _indices = new ushort[buf.Length / 2];
  118. Buffer.BlockCopy(buf, 0, _indices, 0, buf.Length);
  119. _points = new Vertex[points.Length / 3];
  120. for (var primitive = 0; primitive < points.Length / 3; primitive++)
  121. {
  122. var srci = primitive * 3;
  123. _points[primitive] = new Vertex
  124. {
  125. Position = new Vector3(points[srci], points[srci + 1], points[srci + 2])
  126. };
  127. }
  128. for (int i = 0; i < _indices.Length; i += 3)
  129. {
  130. Vector3 a = _points[_indices[i]].Position;
  131. Vector3 b = _points[_indices[i + 1]].Position;
  132. Vector3 c = _points[_indices[i + 2]].Position;
  133. var normal = Vector3.Normalize(Vector3.Cross(c - b, a - b));
  134. _points[_indices[i]].Normal += normal;
  135. _points[_indices[i + 1]].Normal += normal;
  136. _points[_indices[i + 2]].Normal += normal;
  137. }
  138. for (int i = 0; i < _points.Length; i++)
  139. {
  140. _points[i].Normal = Vector3.Normalize(_points[i].Normal);
  141. _maxY = Math.Max(_maxY, _points[i].Position.Y);
  142. _minY = Math.Min(_minY, _points[i].Position.Y);
  143. }
  144. }
  145. }
  146. private void CheckError(GlInterface gl)
  147. {
  148. int err;
  149. while ((err = gl.GetError()) != GL_NO_ERROR)
  150. Console.WriteLine(err);
  151. }
  152. protected unsafe override void OnOpenGlInit(GlInterface GL, int fb)
  153. {
  154. // Load the source of the vertex shader and compile it.
  155. _vertexShader = GL.CreateShader(GL_VERTEX_SHADER);
  156. Console.WriteLine(GL.CompileShaderAndGetError(_vertexShader, VertexShaderSource));
  157. // Load the source of the fragment shader and compile it.
  158. _fragmentShader = GL.CreateShader(GL_FRAGMENT_SHADER);
  159. Console.WriteLine(GL.CompileShaderAndGetError(_fragmentShader, FragmentShaderSource));
  160. // Create the shader program, attach the vertex and fragment shaders and link the program.
  161. _shaderProgram = GL.CreateProgram();
  162. GL.AttachShader(_shaderProgram, _vertexShader);
  163. GL.AttachShader(_shaderProgram, _fragmentShader);
  164. const int positionLocation = 0;
  165. const int normalLocation = 1;
  166. GL.BindAttribLocationString(_shaderProgram, positionLocation, "aPos");
  167. GL.BindAttribLocationString(_shaderProgram, normalLocation, "aNormal");
  168. Console.WriteLine(GL.LinkProgramAndGetError(_shaderProgram));
  169. CheckError(GL);
  170. // Create the vertex buffer object (VBO) for the vertex data.
  171. _vertexBufferObject = GL.GenBuffer();
  172. // Bind the VBO and copy the vertex data into it.
  173. GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject);
  174. var vertexSize = Marshal.SizeOf<Vertex>();
  175. fixed (void* pdata = _points)
  176. GL.BufferData(GL_ARRAY_BUFFER, new IntPtr(_points.Length * vertexSize),
  177. new IntPtr(pdata), GL_STATIC_DRAW);
  178. _indexBufferObject = GL.GenBuffer();
  179. GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject);
  180. fixed (void* pdata = _indices)
  181. GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata),
  182. GL_STATIC_DRAW);
  183. _vertexArrayObject = GL.GenVertexArray();
  184. GL.BindVertexArray(_vertexArrayObject);
  185. GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT,
  186. 0, vertexSize, IntPtr.Zero);
  187. GL.VertexAttribPointer(normalLocation, 3, GL_FLOAT,
  188. 0, vertexSize, new IntPtr(12));
  189. GL.EnableVertexAttribArray(positionLocation);
  190. GL.EnableVertexAttribArray(normalLocation);
  191. CheckError(GL);
  192. }
  193. protected override void OnOpenGlDeinit(GlInterface GL, int fb)
  194. {
  195. // Unbind everything
  196. GL.BindBuffer(GL_ARRAY_BUFFER, 0);
  197. GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  198. GL.BindVertexArray(0);
  199. GL.UseProgram(0);
  200. // Delete all resources.
  201. GL.DeleteBuffers(2, new[] { _vertexBufferObject, _indexBufferObject });
  202. GL.DeleteVertexArrays(1, new[] { _vertexArrayObject });
  203. GL.DeleteProgram(_shaderProgram);
  204. GL.DeleteShader(_fragmentShader);
  205. GL.DeleteShader(_vertexShader);
  206. }
  207. protected override unsafe void OnOpenGlRender(GlInterface gl, int fb)
  208. {
  209. gl.ClearColor(0, 0, 0, 0);
  210. gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  211. gl.Enable(GL_DEPTH_TEST);
  212. gl.Viewport(0, 0, (int)Bounds.Width, (int)Bounds.Height);
  213. var GL = gl;
  214. GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject);
  215. GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject);
  216. GL.BindVertexArray(_vertexArrayObject);
  217. GL.UseProgram(_shaderProgram);
  218. var projection =
  219. Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)(Bounds.Width / Bounds.Height),
  220. 0.01f, 1000);
  221. var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0));
  222. var model = Matrix4x4.CreateFromYawPitchRoll(_yaw, _pitch, _roll);
  223. var modelLoc = GL.GetUniformLocationString(_shaderProgram, "uModel");
  224. var viewLoc = GL.GetUniformLocationString(_shaderProgram, "uView");
  225. var projectionLoc = GL.GetUniformLocationString(_shaderProgram, "uProjection");
  226. var maxYLoc = GL.GetUniformLocationString(_shaderProgram, "uMaxY");
  227. var minYLoc = GL.GetUniformLocationString(_shaderProgram, "uMinY");
  228. GL.UniformMatrix4fv(modelLoc, 1, false, &model);
  229. GL.UniformMatrix4fv(viewLoc, 1, false, &view);
  230. GL.UniformMatrix4fv(projectionLoc, 1, false, &projection);
  231. GL.Uniform1f(maxYLoc, _maxY);
  232. GL.Uniform1f(minYLoc, _minY);
  233. GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero);
  234. CheckError(GL);
  235. }
  236. }
  237. }