SharpGL(C#中的OpenGL)太阳系 - 相机位置计算错误

时间:2016-05-25 19:47:06

标签: c# opengl camera sharpgl

我目前正在学习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度?

1 个答案:

答案 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);