Emotional Code|

Nolca

园龄:6年9个月粉丝:12关注:9

游戏开发入门指引

推荐课程

底层向

笔记

  • 旋转是特殊的剪切

blender Lesson 3 变换矩阵 模拟

L3.blend.zip,记得去掉.zip后打开
image

ui_L3.py备份,建议下载源文件,有几何节点
# type: ignore
import bpy
import math
bl_info = {
"name": "Games101 Visualization",
"author": "AClon",
"description": "Games101教程可视化",
"blender": (2, 80, 0),
"version": (0, 0, 2),
"location": "View3D > Sidebar > Item",
"warning": "",
"category": "Item"
}
def Print(*args):
if False:
print(*args)
def Unit_M(length=4):
return [[1 if i == j else 0 for j in range(length)] for i in range(length)]
def get_col(begin=0):
# Print(f'get_col:{begin}')
obj = bpy.context.object
if not obj:
return None
NG=bpy.data.node_groups["Transform"]
input=NG.nodes["Input Matrix"].inputs
switch = NG.nodes["Switch"].inputs[0]
if switch.default_value:
newV = [None] * 4
for i in range(len(newV)):
newV[i] = bpy.data.node_groups["Transform"].nodes["Input Matrix"].inputs[i+begin*4].default_value
return newV
else:
newV = [ i[begin] for i in obj.matrix_world ]
# for i in range(len(newV)):
# input[i+begin*4].default_value = newV[i] # update only 4
return newV
def set_col(self,newV,begin=0):
Print(f'set_col:{begin}')
NG=bpy.data.node_groups["Transform"]
input=NG.nodes["Input Matrix"].inputs
switch = NG.nodes["Switch"].inputs[0]
if self.shear:
for i in range(len(newV)):
input[i+begin*4].default_value = newV[i] # update only 4
else:
self.shear = True
def update_matrix(input):
Print('update_matrix')
obj = bpy.context.object
if not obj:
return
NG=bpy.data.node_groups["Transform"]
switch = NG.nodes["Switch"].inputs[0]
if switch.default_value:
matrix = obj.matrix_world.copy()
Print(f'switch:{switch.default_value}\nmatrix=\n{matrix}\n{obj.matrix_world}\n')
obj.matrix_world = Unit_M() # 复原
Print(f'switch:{switch.default_value}\nmatrix2:\n{matrix}\n{obj.matrix_world}\n')
for i in range(len(input)):
input[i].default_value = matrix[i%4][i//4]
else:
matrix = Unit_M()
# 找回
for i in range(len(input)):
matrix[i//4][i%4] = input[i].default_value
obj.matrix_world = matrix.copy()
Print(f'switch:{switch.default_value}\nmatrix:\n{matrix}\n')
def update_switch(self, context):
Print('update_switch')
NG=bpy.data.node_groups["Transform"]
input=NG.nodes["Input Matrix"].inputs
switch = NG.nodes["Switch"].inputs[0]
switch.default_value = self.shear
update_matrix(input)
obj = bpy.context.object
# 检测当前有没有几何节点修改器,没有则添加
if not obj.modifiers:
modifier = obj.modifiers.new(type='NODES', name='GN')
# 设置几何节点修改器的节点树为 'Transform'
modifier.node_group = bpy.data.node_groups.get('Transform')
class Get:
def c1(self):
return get_col(0)
def c2(self):
return get_col(1)
def c3(self):
return get_col(2)
def c4(self):
return get_col(3)
class Set:
def c1(self, value):
set_col(self,value,0)
def c2(self, value):
set_col(self,value,1)
def c3(self, value):
set_col(self,value,2)
def c4(self, value):
set_col(self,value,3)
class PropsGroup_games101(bpy.types.PropertyGroup):
shear: bpy.props.BoolProperty(
name='Shear',
default=True,
description='Enable Shear. 开启剪切变换',
update=update_switch
)
c1: bpy.props.FloatVectorProperty(
name='Column 1',
size=4,
default=[1.,0.,0.,0.],
get=Get.c1,
set=Set.c1,
description='',
)
c2: bpy.props.FloatVectorProperty(
name='Column 2',
size=4,
default=[0.,1.,0.,0.],
get=Get.c2,
set=Set.c2,
description='',
)
c3: bpy.props.FloatVectorProperty(
name='Column 3',
size=4,
default=[0.,0.,1.,0.],
get=Get.c3,
set=Set.c3,
description='',
)
c4: bpy.props.FloatVectorProperty(
name='Column 4',
size=4,
default=[0.,0.,0.,1.],
get=Get.c4,
set=Set.c4,
description='',
)
class Panel_games101(bpy.types.Panel):
bl_label = 'GAMES101🎮:矩阵SRT'
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Item'
def draw(self, context):
layout = self.layout
scene = context.scene
props = scene.games101
obj = context.object
row = layout.row()
row.operator(ReloadScriptOperator.bl_idname,
text="", icon='FILE_REFRESH',emboss=False)
row.operator(UnregScriptOperator.bl_idname,
text="", icon='X',emboss=False)
# lock icon button
row.prop(props, "shear", text="", icon='MOD_LATTICE')
col = layout.column(align=True)
row = col.row(align=True)
col_ = row.column(align=True)
col_.prop(props, 'c1')
col_ = row.column(align=True)
col_.prop(props, 'c2')
col_ = row.column(align=True)
col_.prop(props, 'c3')
col_ = row.column(align=True)
col_.prop(props, 'c4')
class UnregScriptOperator(bpy.types.Operator):
bl_idname = 'object.unreg_script'
bl_label = 'Unregister'
bl_options = {'REGISTER'}
bl_description = 'Unregister Script. 移除面板,可以再次运行来重新加载脚本'
def execute(self, context):
unregister()
return {'FINISHED'}
class ReloadScriptOperator(bpy.types.Operator):
bl_idname = 'object.reload_script'
bl_label = 'Reload'
bl_options = {'REGISTER'}
bl_description = 'Reload Script. 重载脚本'
def execute(self, context):
# 获取当前文本编辑器
for area in bpy.context.screen.areas:
if area.type == 'TEXT_EDITOR':
with bpy.context.temp_override(area=area):
bpy.ops.text.resolve_conflict(resolution='RELOAD')
bpy.ops.text.run_script()
break
else:
self.report({'ERROR'}, "没有找到文本编辑器")
return {'CANCELLED'}
return {'FINISHED'}
classes = (
Panel_games101,
PropsGroup_games101,
ReloadScriptOperator,
UnregScriptOperator,
)
def register():
for i in classes:
bpy.utils.register_class(i)
bpy.types.Scene.games101 = bpy.props.PointerProperty(
type=PropsGroup_games101)
def unregister():
del bpy.types.Scene.games101
for i in classes:
bpy.utils.unregister_class(i)
if __name__ == "__main__":
register()

质心坐标,判断某像素是否在三角形内

叉乘/平行四边形/行列式正负

具体来说,叉积的符号由右手法则决定:

如果从第一个向量通过最小角度旋转到第二个向量是逆时针方向,那么叉积的结果是正的。
如果是顺时针方向,那么叉积的结果是负的。


cos 点乘
sin 叉乘

View_camera 勘误纠错

为什么View矩阵内,先平移后旋转?知乎的有点绕。

有了摄像机的整个变换过程 MtMr 。
对于物体的变换,则对上面矩阵求逆,是 (MtMr)1=Mr1Mt1
对于旋转矩阵,正交矩阵的逆=正交矩阵的转置

建议看glm::LookAt的实现
矩阵应用从右到左,给了cam的世界坐标,先平移再旋转;反之,旋转后再平移就不准确了

// glm::View矩阵,难点
Matrix lookat(Vec3f eye, Vec3f center, Vec3f up) {
// 要理解Vec3f存的是 坐标点 还是 向量基,此处存的是 向量基
Vec3f z = (eye - center).normalize(); // 点❌ 方向✅
Vec3f x = (up ^ z).normalize();
Vec3f y = (z ^ x).normalize();
Matrix rot = Matrix::identity(4);
Matrix trans = Matrix::identity(4);
for (int i = 0; i < 3; i++) {
rot[0][i] = x[i];
rot[1][i] = y[i];
rot[2][i] = z[i];
trans[i][3] = -eye[i];
}
return rot * trans; // 顺序很重要
// 矩阵应用从右到左,给了cam的世界坐标,对坐标点做变换:先平移再旋转;反之,旋转后再平移就不准确了
// 对cam向量基做变换:先旋转再平移
// 世界坐标->cam坐标,让世界坐标点 主动出现在 cam的视野(2D屏幕/舞台)内。
}

本文作者:Nolca

本文链接:https://www.cnblogs.com/nolca/p/18564081

版权声明:本作品采用 收益分享revenue sharing 许可协议进行许可。

posted @   Nolca  阅读(24)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 ⏩intro 山姆精
  2. 2 🎸吉他 马叉
  3. 3 ☁升调 山姆精
  4. 4 🐦Flutter Virtual Riot/Madi
  5. 5 🎶纯律 山姆精
  6. 6 👻yeah~Color Bass! VR
⏩intro - 山姆精
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.