我目前正在学习OpenGL,我正在尝试编写类似于教程中的简单太阳能系统应用程序,但由于某种原因,相机行为非常奇怪,我不确定是什么导致了这一点。我希望我的相机能够看到太阳,但我所得到的只是一些非常奇怪的行星角度,或者根本没有任何东西,它甚至可能不是纯粹的相机问题。有人可以告诉我究竟是做错了什么吗?我会很感激一些代码。如果有人愿意帮助并且更愿意检查应用程序而不是在此处阅读代码,则会在下面添加链接。
这里的相机尽可能接近教程(FPS),但我也有拖动/滚动系统而不是这个。
end
Planet.cs:
public class Camera
{
private static float eyeX, eyeY, eyeZ;
private static float centerX, centerY, centerZ;
private const float movingSpeed = 0.3f;
private const float rotationSpeed = 0.25f;
private static double i, j, k;
public static float Height { get; set; }
public static float Slope { get; set; }
public void InitCamera()
{
eyeX = 0f;
eyeY = 15f;
eyeZ = 25f;
centerX = 0;
centerY = 2;
centerZ = 0;
Look();
}
public void Look()
{
Gl.MatrixMode(OpenGL.GL_MODELVIEW);
Gl.LoadIdentity();
Gl.LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);
}
public void UpdateDirVector()
{
i = -Math.Sin(((double)Slope).ToRadians());
j = Math.Sin(((double)Height).ToRadians());
k = Math.Cos(((double)Slope).ToRadians());
centerX = eyeX - (float)i;
centerY = eyeY - (float)j;
centerZ = eyeZ - (float)k;
}
public static void CenterMouse()
{
if (GlCenter == null)
return;
var pos = (Point) GlCenter;
WinApi.SetCursorPos((int)Math.Round(pos.X), (int)Math.Round(pos.Y));
}
public void Update(int pressedButton)
{
if (GlCenter == null)
return;
var pos = (Point)GlCenter;
var halfHeight = GlHeight / 2;
var halfWidth = GlWidth / 2;
var position = new Pointer();
WinApi.GetCursorPos(ref position);
var diffX = (float)pos.X - position.x;
var diffY = (float)pos.Y - position.y;
if (position.y < halfHeight)
Height -= rotationSpeed * diffY;
else if (position.y > halfHeight)
Height += rotationSpeed * -diffY;
if (position.x < halfWidth)
Slope += rotationSpeed * -diffX;
else if (position.x > halfWidth)
Slope -= rotationSpeed * diffX;
UpdateDirVector();
CenterMouse();
if (pressedButton == 1) // LPM
{
eyeX -= (float)i * movingSpeed;
eyeY -= (float)j * movingSpeed;
eyeZ -= (float)k * movingSpeed;
}
else if (pressedButton == -1) // PPM
{
eyeX += (float)i * movingSpeed;
eyeY += (float)j * movingSpeed;
eyeZ += (float)k * movingSpeed;
}
Look();
}
}
Sun.cs
public class Planet
{
private readonly PlanetTypes _planetType;
private readonly Position _position;
private float _orbitAngle;
private readonly float _sizeRadius;
private readonly float _velocity;
private readonly string _texturePath;
private uint _list;
private float _rotationAngle;
public Planet(float radius, PlanetTypes planetType, Position position, string texturePath, bool hasMoon)
{
_sizeRadius = radius;
_planetType = planetType;
_position = position;
_orbitAngle = Rng.Next(360);
_velocity = (float)Rng.NextDouble() * 0.3f;
_texturePath = texturePath;
}
public void Create()
{
var quadric = Gl.NewQuadric();
Gl.QuadricNormals(quadric, OpenGL.GLU_SMOOTH);
Gl.QuadricTexture(quadric, (int) OpenGL.GL_TRUE);
_list = Gl.GenLists(1);
Gl.NewList(_list, OpenGL.GL_COMPILE);
Gl.PushMatrix();
Gl.Rotate(270, 1, 0, 0);
Gl.Sphere(quadric, _sizeRadius, 32, 32);
Gl.PopMatrix();
Gl.EndList();
}
public void DrawOrbit()
{
Gl.Begin(OpenGL.GL_LINE_STRIP);
for (var i = 0; i <= 360; i++)
Gl.Vertex(_position.X * (float)Math.Sin(i * Math.PI / 180), 0, _position.X * (float)Math.Cos(i * Math.PI / 180));
Gl.End();
}
public void Draw()
{
DrawOrbit();
LoadTexture($"{_texturesPath}{_texturePath}");
Gl.PushMatrix();
_orbitAngle += _velocity;
_rotationAngle += 0.6f;
Gl.Rotate(_orbitAngle, 0, 1, 0);
Gl.Translate(-_position.X, -_position.Y, -_position.Z);
Gl.Rotate(_rotationAngle, 0, 1, 0);
Gl.CallList(_list);
Gl.PopMatrix();
}
}
Stars.cs
public class Sun
{
private uint _list;
private float _rotation;
private readonly string _texturePath;
public Sun(string texturePath)
{
_texturePath = texturePath;
}
public void Create()
{
var quadratic = Gl.NewQuadric();
Gl.QuadricNormals(quadratic, OpenGL.GLU_SMOOTH);
Gl.QuadricTexture(quadratic, (int)OpenGL.GL_TRUE);
_list = Gl.GenLists(1);
Gl.NewList(_list, OpenGL.GL_COMPILE);
Gl.PushMatrix();
Gl.Rotate(90, 1, 0, 0);
Gl.Sphere(quadratic, 3, 32, 32);
Gl.PopMatrix();
Gl.EndList();
}
public void Draw()
{
LoadTexture($"{_texturesPath}{_texturePath}");
Gl.PushMatrix();
_rotation += 0.05f;
Gl.Rotate(_rotation, 0, 1, 0);
Gl.CallList(_list);
Gl.PopMatrix();
}
}
SolarSystem.cs(基本上是上面所有内容的集合,枚举不是必需的,我留下它们因为它们将来可能会有用)
public class Stars
{
private readonly List<Position> starPositions = new List<Position>();
public void CreateStars(int amount)
{
var count = 0;
while (count <= amount)
{
var p = default(Position);
p.X = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
p.Z = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
p.Y = Rng.Next(110) * (float)Math.Pow(-1, Rng.Next());
if (!(Math.Pow(Math.Pow(p.X, 2) + Math.Pow(p.Y, 2) + Math.Pow(p.Z, 2), 1 / 3f) > 15))
continue;
starPositions.Add(p);
count++;
}
}
public void Draw()
{
Gl.Begin(OpenGL.GL_POINTS);
Gl.Color(1, 1, 1);
Gl.PointSize(3);
foreach (var starPos in starPositions)
Gl.Vertex(starPos.X, starPos.Y, starPos.Z);
Gl.End();
}
}
API(包括指针位置转换,因为只有这样我才能将光标居中,它会被拖动相机系统删除)):
public class SolarSystem
{
public static Random Rng { get; } = new Random();
private readonly Stars _stars;
private readonly Sun _sun;
private readonly List<Planet> _planets;
public SolarSystem()
{
Camera = new Camera();
_stars = new Stars();
_sun = new Sun("sun.bmp");
_planets = new List<Planet>();
}
public void CreateScene()
{
_planets.Add(new Planet(0.5f, PlanetTypes.Mercury, new Position(5, 0, 0), "mercury.bmp", false)); // tylko tutaj pliki, wszedzie indziej przekształcone na .bmp
_planets.Add(new Planet(0.7f, PlanetTypes.Venus, new Position(11, 0, 0), "venus.bmp", false));
_planets.Add(new Planet(1, PlanetTypes.Earth, new Position(15, 0, 0), "earth.bmp", true));
_planets.Add(new Planet(1, PlanetTypes.Mars, new Position(22, 0, 0), "mars.bmp", false));
_planets.Add(new Planet(1.5f, PlanetTypes.Jupiter, new Position(28, 0, 0), "jupiter.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Saturn, new Position(35, 0, 0), "saturn.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Uranus, new Position(41, 0, 0), "uranus.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Neptune, new Position(51, 0, 0), "neptune.bmp", false));
_planets.Add(new Planet(1.2f, PlanetTypes.Pluto, new Position(60, 0, 0), "pluto.bmp", false));
_stars.CreateStars(500);
_sun.Create();
foreach (var planet in _planets)
planet.Create();
}
public Camera Camera { get; }
public void DrawScene()
{
_stars.Draw();
_sun.Draw();
foreach (var planet in _planets)
planet.Draw();
}
public enum PlanetTypes
{
Mercury,
Venus,
Earth,
Mars,
Jupiter,
Saturn,
Neptune,
Uranus,
Pluto
}
}
Extensions.cs
public static class WinApi
{
[DllImport("GDI32.dll")]
public static extern void SwapBuffers(uint hdc);
[DllImport("user32.dll")]
public static extern void SetCursorPos(int x, int y);
[DllImport("user32.dll")]
public static extern void GetCursorPos(ref Pointer point);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int X;
public int Y;
}
[DllImport("User32", EntryPoint = "ClientToScreen", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ClientToScreen(
IntPtr hWnd,
ref POINT pt);
[EnvironmentPermission(SecurityAction.LinkDemand, Unrestricted = true)]
public static Point? TransformToScreen(
Point point,
Visual relativeTo)
{
var hwndSource = PresentationSource.FromVisual(relativeTo) as HwndSource;
if (hwndSource == null)
return null;
var root = hwndSource.RootVisual;
// Transform the point from the root to client coordinates.
var transformToRoot = relativeTo.TransformToAncestor(root);
var pointRoot = transformToRoot.Transform(point);
var m = Matrix.Identity;
var transform = VisualTreeHelper.GetTransform(root);
if (transform != null)
{
m = Matrix.Multiply(m, transform.Value);
}
var offset = VisualTreeHelper.GetOffset(root);
m.Translate(offset.X, offset.Y);
var pointClient = m.Transform(pointRoot);
pointClient = hwndSource.CompositionTarget.TransformToDevice.Transform(pointClient);
var pointClientPixels = new POINT();
pointClientPixels.X = (0 < pointClient.X)
? (int)(pointClient.X + 0.5)
: (int)(pointClient.X - 0.5);
pointClientPixels.Y = (0 < pointClient.Y)
? (int)(pointClient.Y + 0.5)
: (int)(pointClient.Y - 0.5);
var pointScreenPixels = pointClientPixels;
if (ClientToScreen(
hwndSource.Handle,
ref pointScreenPixels))
{
return new Point(
pointScreenPixels.X,
pointScreenPixels.Y);
}
return new Point();
}
}
MainWindow.cs
public static class Extensions
{
public static double ToDegrees(this double radians)
{
return radians * (180.0 / Math.PI);
}
public static double ToRadians(this double degrees)
{
return Math.PI * degrees / 180.0;
}
}
指向整个应用程序的链接:
https://www.dropbox.com/sh/uhfyeayxn8l7q9y/AAA8tFda5-ZLAjTUzJcwKUm6a?dl=0
更新1:
我已将Look()函数更改为:
public partial class MainWindow
{
private DispatcherTimer _dispatcherTimer;
private uint _hdc;
public static string _texturesPath = @"Data\Textures\";
private SolarSystem _solarSystem;
private int _movement;
public static OpenGL Gl { get; private set; }
public static Point? GlCenter { get; private set; }
public static int GlHeight { get; private set; }
public static int GlWidth { get; private set; }
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
ContentRendered += MainWindow_ContentRendered;
}
private void MainWindow_ContentRendered(object sender, EventArgs e)
{
Gl = openGLControl.OpenGL;
var source = (HwndSource)PresentationSource.FromVisual(openGLControl);
var hWnd = source?.Handle;
if (hWnd != null) _hdc = (uint)hWnd;
_solarSystem = new SolarSystem();
_solarSystem.Camera.InitCamera();
float[] materialAmbient = { 0.5f, 0.5f, 0.5f, 1.0f };
float[] materialDiffuse = { 1f, 1f, 1f, 1.0f };
float[] materialShininess = { 10.0f };
float[] lightPosition = { 0f, 0f, 0f, 1.0f };
float[] lightAmbient = { 0.85f, 0.85f, 0.85f, 0.0f };
Gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightAmbient);
Gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPosition);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_SHININESS, materialShininess);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_DIFFUSE, materialDiffuse);
Gl.Material(OpenGL.GL_FRONT_AND_BACK, OpenGL.GL_AMBIENT, materialAmbient);
Gl.Enable(OpenGL.GL_LIGHTING);
Gl.Enable(OpenGL.GL_LIGHT0);
Gl.Enable(OpenGL.GL_DEPTH_TEST);
_solarSystem.CreateScene();
Gl.ClearColor(0, 0, 0, 1);
GlCenter = WinApi.TransformToScreen(new Point(openGLControl.Width / 2, openGLControl.Height / 2), openGLControl);
GlHeight = (int)openGLControl.Height;
GlWidth = (int)openGLControl.Width;
Camera.CenterMouse();
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick += DispatcherTimer_Tick;
_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
_dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
Gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
_solarSystem.Camera.Update(_movement);
_solarSystem.DrawScene();
WinApi.SwapBuffers(_hdc);
Gl.Flush();
}
private void OpenGLControl_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
_movement = 1;
else
_movement = -1;
}
private void OpenGLControl_MouseUp(object sender, MouseButtonEventArgs e)
{
_movement = 0;
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.C)
{
GlCenter = WinApi.TransformToScreen(new Point(openGLControl.Width / 2, openGLControl.Height / 2), openGLControl); // new Point((int)(Left), (int)(Top));
}
}
public static uint LoadTexture(string filename)
{
if (string.IsNullOrEmpty(filename))
throw new ArgumentException(filename);
Gl.Enable(OpenGL.GL_TEXTURE_2D);
var texture = new uint[1];
var id = texture[0];
Gl.GenTextures(1, texture);
Gl.BindTexture(OpenGL.GL_TEXTURE_2D, id);
var bmp = new Bitmap(filename);
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
Gl.TexImage2D(OpenGL.GL_TEXTURE_2D, 0, 3, bmpData.Width, bmpData.Height, 0, OpenGL.GL_BGR, OpenGL.GL_UNSIGNED_BYTE, bmpData.Scan0);
Gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MIN_FILTER, OpenGL.GL_LINEAR);
Gl.TexParameter(OpenGL.GL_TEXTURE_2D, OpenGL.GL_TEXTURE_MAG_FILTER, OpenGL.GL_LINEAR);
bmp.UnlockBits(bmpData);
return id;
}
}
现在它有效。
现在我可以看到我的应用程序由于某种原因加载了错误的纹理。我猜那是因为我写的方法LoadTextures()是使用Gl.GenTextures(1,纹理)。 (在sharpgl中没有Gen(单一)纹理方法,或者我错了)。
更新2:
所以基本上我的大部分纹理都不起作用,因为它们不是2的力量,但从我所读到的,它们不再是必须的。所以我目前的问题是:如何强制sharpGL显示NPOT纹理?
更新3:
原来我可以像这样加载它们,但是,它们是颠倒的:)。
public void Look()
{
Gl.MatrixMode(OpenGL.GL_PROJECTION);
Gl.LoadIdentity();
Gl.Viewport(0, 0, GlWidth, GlHeight);
Gl.Perspective(45.0f, GlWidth / (double) GlHeight, 1, 200.0);
Gl.LookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);
Gl.MatrixMode(OpenGL.GL_MODELVIEW);
}
更新4:
我可以翻转纹理以正确显示它,但问题是为什么会发生这种情况?
_texture = new Texture();
...
Gl.Enable(OpenGL.GL_TEXTURE_2D);
_texture.Create(Gl, $"{_texturesPath}{_texturePath}");
_texture.Bind(Gl);
更新5:
虽然更新4中的问题仍然存在,但我还有最后一个问题:我如何重新计算相机,因此它不仅限于向上/向下查看-90/90度?
答案 0 :(得分:1)
您似乎没有正确设置您的观看截头,并且正在以未初始化状态使用它。你有代码,但它在MainWindow.xaml.cs中被注释掉:123。
您必须设置截头体。在绘制之前至少一次。它可以是透视或正交。
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.LoadIdentity();
gl.Perspective(60.0f, (double)Width / (double)Height, 0.01, 100.0);