【Filament】绘制立方体

1 前言

        本文主要介绍使用 Filament 绘制彩色立方体,读者如果对 Filament 不太熟悉,请回顾以下内容。

  • Filament环境搭建
  • 绘制三角形
  • 绘制矩形
  • 绘制圆形

2 绘制立方体

        本文项目结构如下,完整代码资源 → Filament绘制立方体。 

2.1 自定义基类

        为方便读者将注意力聚焦在 Filament 的输入上,轻松配置复杂的环境依赖逻辑,笔者仿照 OpenGL ES 的写法,抽出了 FLSurfaceView 和 BaseModel 类。FLSurfaceView 与 GLSurfaceView 的功能类似,承载了渲染环境配置;BaseModel 中提供了一些 VertexBuffer、IndexBuffer、Material、Renderable 相关的工具类,方便子类直接使用这些工具类。

        build.gradle

...
android {
    ...
    aaptOptions { // 在应用程序打包过程中不压缩的文件
        noCompress 'filamat', 'ktx'
    }
}
 
dependencies {
    implementation fileTree(dir: '../libs', include: ['*.aar'])
    ...
}

        说明:在项目根目录下的 libs 目录中,需要放入以下 aar 文件,它们源自Filament环境搭建中编译生成的 aar。  

        FLSurfaceView.java

package com.zhyan8.cube.filament;

import android.content.Context;
import android.graphics.Point;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceView;

import com.google.android.filament.Camera;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.Filament;
import com.google.android.filament.Renderer;
import com.google.android.filament.Scene;
import com.google.android.filament.Skybox;
import com.google.android.filament.SwapChain;
import com.google.android.filament.View;
import com.google.android.filament.Viewport;
import com.google.android.filament.android.DisplayHelper;
import com.google.android.filament.android.FilamentHelper;
import com.google.android.filament.android.UiHelper;

import java.util.ArrayList;

/*
 * Filament中待渲染的SurfaceView
 * 功能可以类比OpenGL ES中的GLSurfaceView
 * 用于创建Filament的渲染环境
 */
public class FLSurfaceView extends SurfaceView {
    public static int RENDERMODE_WHEN_DIRTY = 0; // 用户请求渲染才渲染一帧
    public static int RENDERMODE_CONTINUOUSLY = 1; // 持续渲染
    protected int mRenderMode = RENDERMODE_CONTINUOUSLY; // 渲染模式
    protected Choreographer mChoreographer; // 消息控制
    protected DisplayHelper mDisplayHelper; // 管理Display(可以监听分辨率或刷新率的变化)
    protected UiHelper mUiHelper; // 管理SurfaceView、TextureView、SurfaceHolder
    protected Engine mEngine; // 引擎(跟踪用户创建的资源, 管理渲染线程和硬件渲染器)
    protected Renderer mRenderer; // 渲染器(用于操作系统窗口, 生成绘制命令, 管理帧延时)
    protected Scene mScene; // 场景(管理渲染对象、灯光)
    protected View mView; // 存储渲染数据(View是Renderer操作的对象)
    protected Camera mCamera; // 相机(视角管理)
    protected Point mDesiredSize; // 渲染分辨率
    protected float[] mSkyboxColor; // 背景颜色
    protected SwapChain mSwapChain; // 操作系统的本地可渲染表面(native renderable surface, 通常是一个window或view)
    protected FrameCallback mFrameCallback = new FrameCallback(); // 帧回调
    protected ArrayList<RenderCallback> mRenderCallbacks; // 每一帧渲染前的回调(一般用于处理模型变换、相机变换等)

    static {
        Filament.init();
    }

    public FLSurfaceView(Context context) {
        super(context);
        mChoreographer = Choreographer.getInstance();
        mDisplayHelper = new DisplayHelper(context);
        mRenderCallbacks = new ArrayList<>();
    }

    public void init() { // 初始化
        setupSurfaceView();
        setupFilament();
        setupView();
        setupScene();
    }

    public void setRenderMode(int renderMode) { // 设置渲染模式
        mRenderMode = renderMode;
    }

    public void requestRender() { // 请求渲染
        mChoreographer.postFrameCallback(mFrameCallback);
    }

    public void onResume() { // 恢复
        mChoreographer.postFrameCallback(mFrameCallback);
    }

    public void onPause() { // 暂停
        mChoreographer.removeFrameCallback(mFrameCallback);
    }

    public void onDestroy() { // 销毁Filament环境
        mChoreographer.removeFrameCallback(mFrameCallback);
        mRenderCallbacks.clear();
        mUiHelper.detach();
        mEngine.destroyRenderer(mRenderer);
        mEngine.destroyView(mView);
        mEngine.destroyScene(mScene);
        mEngine.destroyCameraComponent(mCamera.getEntity());
        EntityManager entityManager = EntityManager.get();
        entityManager.destroy(mCamera.getEntity());
        mEngine.destroy();
    }

    public void addRenderCallback(RenderCallback renderCallback) { // 添加渲染回调
        if (renderCallback != null) {
            mRenderCallbacks.add(renderCallback);
        }
    }

    protected void setupScene() { // 设置Scene参数
    }

    protected void onResized(int width, int height) { // Surface尺寸变化时回调
        double zoom = 1;
        double aspect = (double) width / (double) height;
        mCamera.setProjection(Camera.Projection.ORTHO,
                -aspect * zoom, aspect * zoom, -zoom, zoom, 0, 1000);
    }

    private void setupSurfaceView() { // 设置SurfaceView
        mUiHelper = new UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK);
        mUiHelper.setRenderCallback(new SurfaceCallback());
        if (mDesiredSize != null) {
            mUiHelper.setDesiredSize(mDesiredSize.x, mDesiredSize.y);
        }
        mUiHelper.attachTo(this);
    }

    private void setupFilament() { // 设置Filament参数
        mEngine = Engine.create();
        // mEngine = (new Engine.Builder()).featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build();
        mRenderer = mEngine.createRenderer();
        mScene = mEngine.createScene();
        mView = mEngine.createView();
        mCamera = mEngine.createCamera(mEngine.getEntityManager().create());
    }

    private void setupView() { // 设置View参数
        float[] color = mSkyboxColor != null ? mSkyboxColor : new float[] {0, 0, 0, 1};
        Skybox skybox = (new Skybox.Builder()).color(color).build(mEngine);
        mScene.setSkybox(skybox);
        if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
            mView.setPostProcessingEnabled(false); // FEATURE_LEVEL_0不支持post-processing
        }
        mView.setCamera(mCamera);
        mView.setScene(mScene);
    }

    /*
     * 帧回调
     */
    private class FrameCallback implements Choreographer.FrameCallback {
        @Override
        public void doFrame(long frameTimeNanos) { // 渲染每帧数据
            if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
                mChoreographer.postFrameCallback(this); // 请求下一帧
            }
            mRenderCallbacks.forEach(callback -> callback.onCall());
            if (mUiHelper.isReadyToRender()) {
                if (mRenderer.beginFrame(mSwapChain, frameTimeNanos)) {
                    mRenderer.render(mView);
                    mRenderer.endFrame();
                }
            }
        }
    }

    /*
     * Surface回调
     */
    private class SurfaceCallback implements UiHelper.RendererCallback {
        @Override
        public void onNativeWindowChanged(Surface surface) { // Native窗口改变时回调
            if (mSwapChain != null) {
                mEngine.destroySwapChain(mSwapChain);
            }
            long flags = mUiHelper.getSwapChainFlags();
            if (mEngine.getActiveFeatureLevel() == Engine.FeatureLevel.FEATURE_LEVEL_0) {
                if (SwapChain.isSRGBSwapChainSupported(mEngine)) {
                    flags = flags | SwapChain.CONFIG_SRGB_COLORSPACE;
                }
            }
            mSwapChain = mEngine.createSwapChain(surface, flags);
            mDisplayHelper.attach(mRenderer, getDisplay());
        }

        @Override
        public void onDetachedFromSurface() { // 解绑Surface时回调
            mDisplayHelper.detach();
            if (mSwapChain != null) {
                mEngine.destroySwapChain(mSwapChain);
                mEngine.flushAndWait();
                mSwapChain = null;
            }
        }

        @Override
        public void onResized(int width, int height) { // Surface尺寸变化时回调
            mView.setViewport(new Viewport(0, 0, width, height));
            FilamentHelper.synchronizePendingFrames(mEngine);
            FLSurfaceView.this.onResized(width, height);
        }
    }

    public static interface RenderCallback {
        void onCall();
    }
}

        BaseModel.java

package com.zhyan8.cube.filament;

import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.EntityManager;
import com.google.android.filament.IndexBuffer;
import com.google.android.filament.Material;
import com.google.android.filament.MaterialInstance;
import com.google.android.filament.RenderableManager;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.google.android.filament.TransformManager;
import com.google.android.filament.VertexBuffer;
import com.google.android.filament.VertexBuffer.AttributeType;
import com.google.android.filament.VertexBuffer.VertexAttribute;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

/*
 * 模型基类
 * 管理模型的材质、顶点属性、顶点索引、渲染id
 */
public class BaseModel {
    private static String TAG = "BaseModel";
    protected AssetManager mAssetManager; // 资源管理器
    protected Engine mEngine; // Filament引擎
    protected TransformManager mTransformManager; // 模型变换管理器
    protected Material mMaterial; // 模型材质
    protected MaterialInstance mMaterialInstance; // 模型材质实例
    protected VertexBuffer mVertexBuffer; // 顶点属性缓存
    protected IndexBuffer mIndexBuffer; // 顶点索引缓存
    protected int mRenderable; // 渲染id
    protected int mTransformComponent; // 模型变换组件的id
    protected Box mBox; // 渲染区域
    protected FLSurfaceView.RenderCallback mRenderCallback; // 每一帧渲染前的回调(一般用于处理模型变换、相机变换等)

    public BaseModel(AssetManager assetManager, Engine engine) {
        mAssetManager = assetManager;
        mEngine = engine;
        mTransformManager = mEngine.getTransformManager();
    }

    public Material getMaterial() { // 获取材质
        return mMaterial;
    }

    public VertexBuffer getVertexBuffer() { // 获取顶点属性缓存
        return mVertexBuffer;
    }

    public IndexBuffer getIndexBuffer() { // 获取顶点索引缓存
        return mIndexBuffer;
    }

    public int getRenderable() { // 获取渲染id
        return mRenderable;
    }

    public void destroy() { // 销毁模型
        mEngine.destroyEntity(mRenderable);
        mEngine.destroyVertexBuffer(mVertexBuffer);
        mEngine.destroyIndexBuffer(mIndexBuffer);
        mEngine.destroyMaterialInstance(mMaterialInstance);
        mEngine.destroyMaterial(mMaterial);
        EntityManager entityManager = EntityManager.get();
        entityManager.destroy(mRenderable);
    }

    public FLSurfaceView.RenderCallback getRenderCallback() { // 获取渲染回调
        return mRenderCallback;
    }

    protected Material loadMaterial(String materialPath) { // 加载材质
        Buffer buffer = readUncompressedAsset(mAssetManager, materialPath);
        if (buffer != null) {
            Material material = (new Material.Builder()).payload(buffer, buffer.remaining()).build(mEngine);
            mMaterialInstance = material.createInstance();
            material.compile(
                    Material.CompilerPriorityQueue.HIGH,
                    Material.UserVariantFilterBit.ALL,
                    new Handler(Looper.getMainLooper()),
                    () -> Log.i(TAG, "Material " + material.getName() + " compiled."));
            mEngine.flush();
            return material;
        }
        return null;
    }

    protected VertexBuffer getVertexBuffer(float[] values) { // 获取顶点属性缓存
        ByteBuffer vertexData = getByteBuffer(values);
        int vertexCount = values.length / 3;
        int vertexSize = Float.BYTES * 3;
        VertexBuffer vertexBuffer = new VertexBuffer.Builder()
                .bufferCount(1)
                .vertexCount(vertexCount)
                .attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
                .build(mEngine);
        vertexBuffer.setBufferAt(mEngine, 0, vertexData);
        return vertexBuffer;
    }

    protected VertexBuffer getVertexBuffer(Vertex[] values) { // 获取顶点属性缓存
        ByteBuffer vertexData = getByteBuffer(values);
        int vertexCount = values.length;
        int vertexSize = Vertex.BYTES;
        VertexBuffer vertexBuffer = new VertexBuffer.Builder()
                .bufferCount(1)
                .vertexCount(vertexCount)
                .attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
                .attribute(VertexAttribute.COLOR,    0, AttributeType.UBYTE4, 3 * Float.BYTES, vertexSize)
                .normalized(VertexAttribute.COLOR)
                .build(mEngine);
        vertexBuffer.setBufferAt(mEngine, 0, vertexData);
        return vertexBuffer;
    }

    protected IndexBuffer getIndexBuffer(short[] values) { // 获取顶点索引缓存
        ByteBuffer indexData = getByteBuffer(values);
        int indexCount = values.length;
        IndexBuffer indexBuffer = new IndexBuffer.Builder()
                .indexCount(indexCount)
                .bufferType(IndexBuffer.Builder.IndexType.USHORT)
                .build(mEngine);
        indexBuffer.setBuffer(mEngine, indexData);
        return indexBuffer;
    }

    protected int getRenderable(PrimitiveType primitiveType, int vertexCount) { // 获取渲染id
        int renderable = EntityManager.get().create();
        new RenderableManager.Builder(1)
                .boundingBox(mBox)
                .geometry(0, primitiveType, mVertexBuffer, mIndexBuffer, 0, vertexCount)
                .material(0, mMaterialInstance)
                .build(mEngine, renderable);
        return renderable;
    }

    private Buffer readUncompressedAsset(AssetManager assetManager, String assetName) { // 加载资源
        ReadableByteChannel src = null;
        ByteBuffer dist = null;
        try {
            AssetFileDescriptor fd = assetManager.openFd(assetName);
            FileInputStream fis = fd.createInputStream();
            dist = ByteBuffer.allocate((int) fd.getLength());
            src = Channels.newChannel(fis);
            src.read(dist);
        } catch (IOException e) {
            Log.d(TAG, "readUncompressedAsset-catch, e=" + e);
        } finally {
            if (src != null) {
                try {
                    src.close();
                } catch (IOException e) {
                    Log.d(TAG, "readUncompressedAsset-finally, e=" + e);
                }
            }
        }
        if (dist != null) {
            return dist.rewind();
        }
        return null;
    }

    private ByteBuffer getByteBuffer(float[] values) { // float数组转换为ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Float.BYTES);
        byteBuffer.order(ByteOrder.nativeOrder());
        for (int i = 0; i < values.length; i++) {
            byteBuffer.putFloat(values[i]);
        }
        byteBuffer.flip();
        return byteBuffer;
    }

    private ByteBuffer getByteBuffer(short[] values) { // short数组转换为ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Short.BYTES);
        byteBuffer.order(ByteOrder.nativeOrder());
        for (int i = 0; i < values.length; i++) {
            byteBuffer.putShort(values[i]);
        }
        byteBuffer.flip();
        return byteBuffer;
    }

    private ByteBuffer getByteBuffer(Vertex[] values) { // Vertex数组转换为ByteBuffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(values.length * Vertex.BYTES);
        byteBuffer.order(ByteOrder.nativeOrder());
        for (int i = 0; i < values.length; i++) {
            values[i].put(byteBuffer);
        }
        byteBuffer.flip();
        return byteBuffer;
    }

    /*
     * 顶点数据
     * 包含顶点位置和颜色
     */
    public static class Vertex {
        public static int BYTES = 16;
        public float x;
        public float y;
        public float z;
        public int color;
        public Vertex() {}
        public Vertex(float x, float y, float z, int color) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.color = color;
        }

        public ByteBuffer put(ByteBuffer buffer) { // Vertex转换为ByteBuffer
            buffer.putFloat(x);
            buffer.putFloat(y);
            buffer.putFloat(z);
            buffer.putInt(color);
            return buffer;
        }
    }
}

2.2 绘制彩色立方体

        MainActivity.java

package com.zhyan8.cube;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

import com.zhyan8.cube.filament.FLSurfaceView;

public class MainActivity extends AppCompatActivity {
    private FLSurfaceView mFLSurfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mFLSurfaceView = new MyFLSurfaceView(this);
        setContentView(mFLSurfaceView);
        mFLSurfaceView.init();
        mFLSurfaceView.setRenderMode(FLSurfaceView.RENDERMODE_CONTINUOUSLY);
    }

    @Override
    public void onResume() {
        super.onResume();
        mFLSurfaceView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mFLSurfaceView.onPause();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mFLSurfaceView.onDestroy();
    }
}

        MyFLSurfaceView.java

package com.zhyan8.cube;

import android.content.Context;

import com.google.android.filament.Camera;
import com.zhyan8.cube.filament.BaseModel;
import com.zhyan8.cube.filament.FLSurfaceView;

public class MyFLSurfaceView extends FLSurfaceView {
    private BaseModel mMyModel;
    public MyFLSurfaceView(Context context) {
        super(context);
    }

    public void init() {
        mSkyboxColor = new float[] {0.965f, 0.941f, 0.887f, 1};
        super.init();
    }

    @Override
    public void onDestroy() {
        mMyModel.destroy();
        super.onDestroy();
    }

    @Override
    protected void setupScene() { // 设置Scene参数
        mMyModel = new Cube(getContext().getAssets(), mEngine);
        mScene.addEntity(mMyModel.getRenderable());
        addRenderCallback(mMyModel.getRenderCallback());
    }

    @Override
    protected void onResized(int width, int height) {
        double zoom = 0.3;
        double aspect = (double) width / (double) height;
        mCamera.setProjection(Camera.Projection.PERSPECTIVE,
                -aspect * zoom, aspect * zoom, -zoom, zoom, 0.3, 100);
        float[] eye = new float[] {1, 1, 1.5f};
        float[] center = new float[] {0, 0, 0};
        float[] up = new float[] {0, 1, 0};
        mCamera.lookAt(eye[0], eye[1], eye[2],center[0], center[1], center[2], up[0], up[1], up[2]);
    }
}

        Cube.java

package com.zhyan8.cube;

import android.content.res.AssetManager;
import android.opengl.Matrix;

import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.RenderableManager.PrimitiveType;
import com.zhyan8.cube.filament.BaseModel;

public class Cube extends BaseModel {
    private String materialPath = "materials/cube.filamat";
    private Vertex[] mVertices; // 顶点坐标
    private short[] mIndex; // 顶点索引
    private float[] mModelMatrix; // 模型变换矩阵
    private float mAngle = 0; // 旋转角度

    public Cube(AssetManager assetManager, Engine engine) {
        super(assetManager, engine);
        init();
    }

    private void init() {
        mVertices = getVertices(0.5f);
        mIndex = getIndices();
        mBox = new Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.01f);
        mMaterial = loadMaterial(materialPath);
        mVertexBuffer = getVertexBuffer(mVertices);
        mIndexBuffer = getIndexBuffer(mIndex);
        mRenderable = getRenderable(PrimitiveType.TRIANGLES, mIndex.length);
        mTransformComponent = mTransformManager.getInstance(mRenderable);
        mRenderCallback = () -> renderCallback();
        mModelMatrix = new float[16];
    }

    private void renderCallback() {
        mAngle = (mAngle + 2) % 360;
        Matrix.setRotateM(mModelMatrix, 0, mAngle, 0.0f, 1.0f, 0.0f);
        mTransformManager.setTransform(mTransformComponent, mModelMatrix);
    }

    private Vertex[] getVertices(float r) { // 获取顶点数据
        Vertex[] vertex = new Vertex[] {
                new Vertex(r, r, r, 0xff0000ff), //0
                new Vertex(-r, r, r, 0xff00ff00), //1
                new Vertex(-r, -r, r, 0xff0f0f00), //2
                new Vertex(r, -r, r, 0xfff0f000), //3
                new Vertex(r, r, -r, 0xffff0000), //4
                new Vertex(-r, r, -r, 0xfff000f0), //5
                new Vertex(-r, -r, -r, 0xff00f0f0), //6
                new Vertex(r, -r, -r, 0xff000f0f) //7
        };
        return vertex;
    }

    private short[] getIndices() { // 获取三角形顶点索引序列
        short[] indices = new short[] {
                0, 1, 2, 0, 2, 3, //前面
                0, 5, 1, 0, 4, 5, //上面
                0, 3, 7, 0, 7, 4, //右面
                6, 5, 4, 6, 4, 7, //后面
                6, 3, 2, 6, 7, 3, //下面
                6, 2, 1, 6, 1, 5 //左面
        };
        return indices;
    }
}

        cube.mat

material {
    name : cube,

    // 顶点着色器入参MaterialVertexInputs中需要的顶点属性
    requires : [
        color
    ],

    // 禁用所有lighting
    shadingModel : unlit,
    featureLevel : 0
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material); // 在方法返回前必须回调该函数
        material.baseColor = getColor();
    }
}

        transform.bat

@echo off
setlocal enabledelayedexpansion
set "srcFolder=../src/main/materials"
set "distFolder=../src/main/assets/materials"

for %%f in ("%srcFolder%\*.mat") do (
	set "matfile=%%~nf"
	matc -p mobile -a opengl -o "!matfile!.filamat" "%%f"
    move "!matfile!.filamat" "%distFolder%\!matfile!.filamat"
)

echo Processing complete.
pause

        说明:需要将 matc.exe 文件与 transform.bat 文件放在同一个目录下面,matc.exe 源自Filament环境搭建中编译生成的 exe 文件。双击 transform.bat 文件,会自动将 /src/main/materials/ 下面的所有 mat 文件全部转换为 filamat 文件,并移到 /src/main/assets/materials/ 目录下面。

        运行效果如下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/264222.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【计算机系统结构实验】实验5 多核编程(OpenMP编程)

5.1 实验目的 加深对多核处理器架构的理解&#xff1b; 掌握使用OpenMP进行多线程编程的基本方法&#xff1b; 学习Windows和OpenEuler环境下多核编程的过程和time命令&#xff1b; 5.2 实验平台 需要多核处理器的计算机和微软编程工具Visual Studio 2012。Taishan服务器&…

Mac使用Vmware Fusion虚拟机配置静态ip地址

一、设置虚拟机的网络为NAT 二、修改虚拟机的网络适配器网络 1、查看虚拟机的网卡 cd /etc/sysconfig/network-scripts#有些系统显示的是ens33&#xff0c;ens160等等 #不同的系统和版本&#xff0c;都会有所不同 #Centos8中默认是ens160,在RedHat/Centos7则为ens33 2、查看网…

封装Detours用于Python中x64函数hook

Detours 代码仓库: https://github.com/microsoft/Detours x64写一个任意地址hook要比x86麻烦的多&#xff0c;所以这里直接封装框架来用于x64的hook。 Detours是微软发布的一个API hook框架&#xff0c;同时支持x86和x64&#xff0c;看文档说也支持ARM和ARM64的Windows。 …

【Linux笔记】网络操作命令详细介绍

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux学习 ⛳️ 功不唐捐&#xff0c;玉汝于成 前言&#xff1a; 网络操作是Linux系统中常见的任务之一&#xff0c;它涵盖了测试网络连接、配置网络接口、显示网络统计信息以及远程登录和文件传…

STM32的以太网外设+PHY(LAN8720)使用详解(5):MAC及DMA配置

0 工具准备 1.野火 stm32f407霸天虎开发板 2.LAN8720数据手册 3.STM32F4xx中文参考手册1 MAC及DMA配置 1.1 使能ETH时钟 stm32的ETH外设挂载在AHB1总线上&#xff0c;位于RCC_AHB1ENR的bit25-bit27&#xff1a; 相关语句如下&#xff1a; RCC_AHB1PeriphClockCmd(RCC_AHB1…

WPF组合控件TreeView+DataGrid之DataGrid封装-粉丝专栏

wpf的功能非常强大&#xff0c;很多控件都是原生的&#xff0c;但是要使用TreeViewDataGrid的组合&#xff0c;就需要我们自己去封装实现。 我们需要的效果如图所示&#xff1a; 这2个图都是第三方控件自带的&#xff0c;并且都是收费使用。 现在我们就用原生的控件进行封装一…

Tekton

一. 概念 Tekton 官网 Github Tekton 是一种用于构建 CI/CD 管道的云原生解决方案&#xff0c;它由提供构建块的 Tekton Pipelines&#xff0c;Tekton 作为 Kubernetes 集群上的扩展安装和运行&#xff0c;包含一组 Kubernetes 自定义资源&#xff0c;这些资源定义了您可以为…

nodejs+vue+ElementUi资源互助共享平台的设计

后台&#xff1a;管理员功能有个人中心&#xff0c;用户管理&#xff0c;卖家管理&#xff0c;咨询师管理&#xff0c;萌宝信息管理&#xff0c;幼儿知识管理&#xff0c;保姆推荐管理&#xff0c;音频资源管理&#xff0c;二手商品管理&#xff0c;商品分类管理&#xff0c;资…

【CF闯关练习】—— 900分段

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;cf闯关练习 &#x1f48c;其他专栏&#xff1a; &#x1f534;每日一题 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓…

GPTs | Actions应用案例

上篇文章说道&#xff0c;如何使用创建的GPTs通过API接口去获取外部的一些信息&#xff0c;然后把获取的外部信息返回给ChatGPT让它加工出来&#xff0c;回答你的问题&#xff0c;今天我们就来做一个通俗易懂的小案例&#xff0c;让大家来初步了解一下它的使用法&#xff01; …

基于AE、VAE 和 VQ-VAE的图像生成

AE 是将数据映直接映射为数值code&#xff08;确定的数值&#xff09;&#xff0c;主要用于图像压缩与还原VAE是先将数据映射为分布&#xff0c;再从分布中采样得到数值code&#xff0c;主要用于图像生成。AQ-VAE是在原始VAE基础上多了一步Vector Quantization矢量量化操作&…

STM32的以太网外设+PHY(LAN8720)使用详解(1):ETH和PHY介绍

0 工具准备 1.野火 stm32f407霸天虎开发板 2.LAN8720数据手册 3.STM32F4xx中文参考手册1 以太网外设&#xff08;ETH&#xff09; 1.1 以太网外设&#xff08;ETH&#xff09;简介 STM32F4系列MCU内部集成了一个以太网外设&#xff08;ETH&#xff09;&#xff0c;可以通过介…

DRF之引入

目录 一、web应用模式 【1】前后端混合开发 【2】前后端分离 二、API接口 三、接口测试工具&#xff1a;Postman 四、RESTful API规范 【1】什么是RESTful 【2】RESTful API的规范 2.1 数据的安全保障 2.2 接口特征表现 2.3 多数据版本共存 2.4 数据即是资源&#…

2024年PMP考试新考纲-PMBOK第七版-项目管理原则真题解析

从战争中学习战争。对于参加2024年PMP考试的小伙伴来说&#xff0c;最有效的学习方式是这样地&#xff1a;①阅读了教材&#xff08;PMBOK6、7和敏捷&#xff09;&#xff0c;了解基本概念&#xff1b;②反复刷近期的PMP考试真题&#xff0c;查漏补缺。 为此&#xff0c;华研荟…

华为云Stack 8.X流量模型分析(三)

三、VPC内部二层流量模型分析 1.不同宿主机下虚拟机互访 VM1发送arp请求&#xff0c;arp报文根据流表到达br-tun&#xff0c;br-tun给予VM1到达VM2的MAC信息。此时arp报文不出宿主机&#xff08;Host1&#xff09;&#xff1b; **注意&#xff1a;**br-tun内的信息是由管理平…

2023年PMP证书的含金量有多高?对于企业来说有多大的价值?

PMP含金量更多的是“敲门砖”作用&#xff0c;公司招聘的门槛 当然现在PMP管理模式也很热门&#xff0c;各大企业都有引进改良应用在公司的项目上&#xff0c;之前在校友群里面大家在讨论PMP 的作用也有说到这一点&#xff0c;给大家看看吧。 至于为什么PMP认证从国外引进大陆…

尺寸公差分析与尺寸链计算软件-DTAS3D到底能给我们带来哪些价值?

【技能】DTAS3D能给我们带来哪些价值&#xff1f; DTAS3D是一款高度集成的公差分析软件&#xff0c;旨在为产品开发团队提供准确的建议&#xff0c;从而放心地将设计发布给制造部门。下面是DTAS3D的关键价值和应用: 1.与三维CAD无缝集成: DTAS3D与三维CAD软件 (CATIA、NX、Cr…

如何进行USB丢弃攻击?

USB丢弃攻击&#xff0c;类似于一场表演艺术&#xff0c;您需要构建一个引人入胜的故事&#xff0c;激发目标的好奇心&#xff0c;让他们忽略基本的安全意识&#xff0c;插入您精心准备的USB设备! 本文章仅限娱乐&#xff0c;请勿模仿或进行违法活动&#xff01; 一、选择放置…

提示“由于找不到mfc140u.dll,无法继续执行代码”如何解决?

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是找不到某个动态链接库文件&#xff0c;比如mfc140u.dll。这个问题可能会导致某些应用程序无法正常运行或打开。 一、关于找不到mfc140u.dll文件造成会的问题 mfc140u.dll是Visual C中的一个…

深眸科技以AI+3D视觉技术引领技术创新,赋予工业自动化新的活力

随着工业4.0和智能制造时代的到来&#xff0c;3D机器视觉在工业各领域的应用越来越重要。这种技术改变了传统工业的生产方式&#xff0c;为现代工业带来了更高的生产效率和更精确的质量控制&#xff0c;广泛涉及物体识别、产品检测、尺寸测量、视觉引导定位等环节。 在工业领域…