Opengl - 旋转、缩放、平移 [英] Opengl - rotation, scale, translation
问题描述
问题:使用 opengl 进行渲染时应用旋转、缩放和平移的顺序是什么?
Question: What is the order to apply rotation, scale, and translation for rendering using opengl?
目前我正在运行代码
- GL11.glScaled(比例值)
- GL11.glRotated(x)//间距
- GL11.glRotated(y)//yaw
- GL11.glRotated(z)//滚动
- GL11.glTranslated(x, y, z)
我尝试过更改顺序,结果有很多不同.一些配置改进了渲染,例如,最后缩放不会干扰翻译.然而,它似乎打破了旋转,导致俯仰和滚动同时滚动模型.
I've tried changing the order with a lot of different results. Some configuration improve the rendering, for example, scaling last doesn't mess with the translation. However, it seems to break the rotation causing pitch and roll to both roll the model.
一些额外信息:该代码适用于使用 JSON 数据呈现项目的 Minecraft mod.可以在这里找到确切的代码:https://github.com/VoltzEngine-Project/Engine/blob/development/src/main/scala/com/builtbroken/mc/client/json/render/state/ModelState.java#L48
Some extra information: The code is for a Minecraft mod that renders items using JSON data. The exact code can be found here: https://github.com/VoltzEngine-Project/Engine/blob/development/src/main/scala/com/builtbroken/mc/client/json/render/state/ModelState.java#L48
根据 BDL 的要求添加到帖子中的代码
Code added to post by request of BDL
package com.builtbroken.mc.client.json.render.state;
import com.builtbroken.jlib.helpers.MathHelper;
import com.builtbroken.mc.client.SharedAssets;
import com.builtbroken.mc.client.json.ClientDataHandler;
import com.builtbroken.mc.client.json.imp.IModelState;
import com.builtbroken.mc.client.json.models.ModelData;
import com.builtbroken.mc.client.json.texture.TextureData;
import com.builtbroken.mc.imp.transform.rotation.EulerAngle;
import com.builtbroken.mc.imp.transform.vector.Pos;
import cpw.mods.fml.client.FMLClientHandler;
import org.lwjgl.opengl.GL11;
/**
* Render/Texture/Animation states used for rendering models in the game
*
* @see <a href="https://github.com/BuiltBrokenModding/VoltzEngine/blob/development/license.md">License</a> for what you can and can't do with the code.
* Created by Dark(DarkGuardsman, Robert) on 11/22/2016.
*/
public class ModelState extends TextureState implements IModelState
{
public String modelID;
public String[] parts;
public Pos offset;
public Pos scale;
public EulerAngle rotation;
public boolean renderParent = false;
public boolean renderOnlyParts = true;
public ModelState(String ID)
{
super(ID);
}
public ModelState(String ID, String modelID, Pos offset, Pos scale, EulerAngle rotation)
{
this(ID);
this.modelID = modelID;
this.offset = offset;
this.scale = scale;
this.rotation = rotation;
}
@Override
public boolean render(boolean subRender, float yaw, float pitch, float roll)
{
TextureData textureData = getTexture(); //Texture reference, texture is .png loaded using property code owned by Mojang so can not be shared
ModelData data = getModel(); //Model reference, model is .obj rendered using triangles
if (data != null && data.getModel() != null)
{
//Starts rendering by storing previous matrix
GL11.glPushMatrix();
if (!subRender)
{
//TODO handle parent additions, in which parent and child data are combined
//Scales object by value
if (scale != null)
{
GL11.glScaled(scale.x(), scale.y(), scale.z());
}
else if (parentState instanceof IModelState && ((IModelState) parentState).getScale() != null)
{
GL11.glScaled(((IModelState) parentState).getScale().x(), ((IModelState) parentState).getScale().y(), ((IModelState) parentState).getScale().z());
}
//Rotates object, needs to be handled after scaling
if (rotation != null)
{
GL11.glRotated(MathHelper.clampAngleTo360(rotation.pitch() + pitch), 1, 0, 0);
GL11.glRotated(MathHelper.clampAngleTo360(rotation.yaw() + yaw), 0, 1, 0);
GL11.glRotated(MathHelper.clampAngleTo360(rotation.roll() + roll), 0, 0, 1);
}
else if (parentState instanceof IModelState && ((IModelState) parentState).getRotation() != null)
{
GL11.glRotated(MathHelper.clampAngleTo360(((IModelState) parentState).getRotation().pitch() + pitch), 1, 0, 0);
GL11.glRotated(MathHelper.clampAngleTo360(((IModelState) parentState).getRotation().yaw() + yaw), 0, 1, 0);
GL11.glRotated(MathHelper.clampAngleTo360(((IModelState) parentState).getRotation().roll() + roll), 0, 0, 1);
}
//Moves the object
if (offset != null)
{
GL11.glTranslated(offset.x(), offset.y(), offset.z());
}
else if (parentState instanceof IModelState && ((IModelState) parentState).getOffset() != null)
{
GL11.glTranslated(((IModelState) parentState).getOffset().x(), ((IModelState) parentState).getOffset().y(), ((IModelState) parentState).getOffset().z());
}
}
//Render parent
GL11.glPushMatrix();
if (parentState instanceof IModelState)
{
if (renderParent)
{
((IModelState) parentState).render(true);
}
else if (parts == null && parentState instanceof ModelState && ((ModelState) parentState).renderParent)
{
if (((ModelState) parentState).parentState instanceof IModelState)
{
((IModelState) ((ModelState) parentState).parentState).render(true);
}
}
}
GL11.glPopMatrix();
//Binds texture
if (textureData != null)
{
FMLClientHandler.instance().getClient().renderEngine.bindTexture(textureData.getLocation());
}
else
{
//Backup texture bind, if no texture
FMLClientHandler.instance().getClient().renderEngine.bindTexture(SharedAssets.GREY_TEXTURE);
}
//Render model
data.render(renderOnlyParts, getPartsToRender());
//Ends render by restoring previous matrix(rotation, position, etc)
GL11.glPopMatrix();
return true;
}
return false;
}
@Override
public Pos getScale()
{
if (scale == null)
{
return parentState instanceof IModelState ? ((IModelState) parentState).getScale() : null;
}
else if (parentState instanceof IModelState)
{
//TODO add to parent rotation, or null out rotation
//TODO setup logic via configs to allow users to decide how rotation is used
}
return scale;
}
@Override
public Pos getOffset()
{
if (offset == null)
{
return parentState instanceof IModelState ? ((IModelState) parentState).getOffset() : null;
}
else if (parentState instanceof IModelState)
{
//TODO add to parent rotation, or null out rotation
//TODO setup logic via configs to allow users to decide how rotation is used
}
return offset;
}
@Override
public EulerAngle getRotation()
{
if (rotation == null)
{
return parentState instanceof IModelState ? ((IModelState) parentState).getRotation() : null;
}
else if (parentState instanceof IModelState)
{
//TODO add to parent rotation, or null out rotation
//TODO setup logic via configs to allow users to decide how rotation is used
}
return rotation;
}
@Override
public ModelData getModel()
{
if (parentState instanceof IModelState)
{
return ((IModelState) parentState).getModel();
}
return ClientDataHandler.INSTANCE.getModel(modelID);
}
@Override
public String[] getPartsToRender()
{
if (parentState instanceof IModelState && (parts == null || parts.length == 0))
{
return ((IModelState) parentState).getPartsToRender();
}
return parts;
}
@Override
public TextureData getTexture()
{
TextureData textureData = ClientDataHandler.INSTANCE.getTexture(textureID);
if (textureData == null && parentState instanceof IModelState)
{
return ((IModelState) parentState).getTexture();
}
return textureData;
}
}
推荐答案
对于通常的用例,请遵循以下操作顺序:缩放 -> 旋转 -> 平移
Follow this order of operations for the usual use case: Scale -> Rotate -> Translate
对于旋转本身,一个直观的约定是先滚动,然后是俯仰,最后是偏航.滚动沿对象的轴发生.向上或向下倾斜滚动的物体.然后水平定向模型.
For the rotation itself, an intuitive convention is first roll, then pitch and at the end yaw. Rolling happens along an axis of the object. Pitch up or down the rolled object. Then horizontally orient the model.
缩放发生在对象模型坐标中.缩放发生后旋转,因此您可以相对于场景帧旋转缩放模型.最后,您最后使用平移将生成的转换对象放置到位.
Scaling happens in object model coordinates. Rotation after the scale happens so you're rotating the scaled model with respect to the scene frame. Eventually you use translation last to place the resulting transformed object into place.
有时您需要进行前一轮的缩放和旋转,以解决工具/引擎在右手、坐标(Z 向上或 Y 向上)和缩放方面的差异
You sometimes need to do a previous round of scale and rotation to account for the tool/engine differences in right-handedness, coordinates (Z is up or Y is up.) and scale
这篇关于Opengl - 旋转、缩放、平移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!