游戏开发入门指引
推荐课程
底层向
- GAMES 101/202... + openGL迷你渲染器tinyRender + 补充笔记
- 【双语】【TheCherno】游戏引擎
笔记
- 旋转是特殊的剪切
blender Lesson 3 变换矩阵 模拟
L3.blend.zip,记得去掉.zip
后打开
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 。
对于物体的变换,则对上面矩阵求逆,是
对于旋转矩阵,正交矩阵的逆=正交矩阵的转置
建议看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屏幕/舞台)内。 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步