尊敬的开发者,您好
关于您反馈的问题
如下代码,设置DirectionalLight的rotation属性可以明显观察到立方体的阴影变化。
import {
Scene,
RenderContext,
SceneResourceParameters,
CustomGeometry,
RenderResourceFactory,
LightType,
MaterialType,
Material,
PrimitiveTopology,
SceneResourceFactory,
Camera,
Node,
Container,
MeshResource,
Quaternion,
DirectionalLight,
Geometry
} from '@kit.ArkGraphics3D';
@Entry
@Component
struct ContainerPage {
@State sceneOpt: SceneOptions | null = null;
@State hierarchy: string = '';
scene: Scene | null = null;
cam: Camera | null = null;
dirLight: DirectionalLight | null = null;
geometryNode: Geometry | null = null;
traversal(node: Node | null): void {
if (!node) {
return;
}
this.hierarchy += node.path + (node.name ? ` [${node.name}]` : '') + '\n';
const container: Container<Node> = node.children;
const count: number = container.count();
this.hierarchy += ' ';
for (let i = 0; i < count; i++) {
this.traversal(container.get(i));
}
}
aboutToAppear(): void {
this.init();
}
aboutToDisappear(): void {
if (this.scene) {
this.scene.destroy();
this.scene = null;
}
this.cam = null;
this.dirLight = null;
this.geometryNode = null;
}
init(): void {
if (this.scene === null) {
const renderContext: RenderContext | null = Scene.getDefaultRenderContext();
if (!renderContext) {
console.error('获取默认渲染上下文失败');
return;
}
const renderResourceFactory: RenderResourceFactory = renderContext.getRenderResourceFactory();
renderResourceFactory.createScene().then(async (result: Scene) => {
if (!result || !result.root) {
console.error('创建场景失败');
return;
}
this.scene = result;
const rf: SceneResourceFactory = this.scene.getResourceFactory();
this.cam = await rf.createCamera({ name: 'MainCamera' });
this.cam.enabled = true;
this.cam.position = { x: 0, y: 2, z: 5 };
this.cam.fov = 60 * Math.PI / 180;
this.cam.nearPlane = 0.1;
this.cam.farPlane = 100;
this.scene.root?.children.append(this.cam);
this.dirLight = await rf.createLight(
{ name: 'Sun' },
LightType.DIRECTIONAL
) as DirectionalLight;
this.dirLight.enabled = true;
this.dirLight.color = {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0
};
this.dirLight.intensity = 2.0;
this.dirLight.shadowEnabled = true;
this.dirLight.rotation = eulerToQuaternion(45, 0, 0);
this.scene.root?.children.append(this.dirLight);
const meshResource = await createMeshResource();
this.geometryNode = await rf.createGeometry(
{ name: 'cubeGeometry' },
meshResource
);
const pbrMat: Material = await rf.createMaterial(
{ name: 'pbrMat' },
MaterialType.METALLIC_ROUGHNESS
);
this.geometryNode.mesh.subMeshes[0].material = pbrMat;
this.scene.root?.children.append(this.geometryNode);
this.hierarchy = '';
this.traversal(this.scene.root);
this.sceneOpt = {
scene: this.scene,
modelType: ModelType.SURFACE
} as SceneOptions;
}).catch((reason: string) => {
console.error(`场景初始化失败: ${reason}`);
});
}
}
rotateLight(degrees: number): void {
if (!this.dirLight) {
return;
}
const deltaRot = eulerToQuaternion(0, degrees, 0); // 绕Y轴旋转
this.dirLight.rotation = multiplyQuaternions(this.dirLight.rotation, deltaRot);
}
// 重置平行光
resetLight(): void {
if (!this.dirLight) {
return;
}
this.dirLight.rotation = eulerToQuaternion(45, 0, 0); // 恢复初始方向
}
build() {
Column() {
Column() {
if (this.sceneOpt) {
Component3D(this.sceneOpt)
.renderWidth('100%')
.renderHeight('100%')
} else {
Text('3D场景加载中...')
.fontSize(20)
.fontColor(Color.Gray)
}
}
.height('50%')
.width('100%')
.backgroundColor(Color.White)
Row({ space: 16 }) {
Button('向左旋转45°')
.onClick(() => this.rotateLight(-45))
.fontSize(14)
Button('向右旋转45°')
.onClick(() => this.rotateLight(45))
.fontSize(14)
Button('重置方向')
.onClick(() => this.resetLight())
.fontSize(14)
}
.padding(16)
.justifyContent(FlexAlign.Center)
Column() {
Text('场景节点层次:')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 8 })
Scroll() {
Text(this.hierarchy)
.fontSize(12)
.fontColor(Color.Gray)
.lineHeight(20)
}
.height('100%')
}
.height('30%')
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.height('100%')
.backgroundColor(Color.Gray)
}
}
function eulerToQuaternion(x: number, y: number, z: number): Quaternion {
const xRad = x * Math.PI / 180;
const yRad = y * Math.PI / 180;
const zRad = z * Math.PI / 180;
const cx = Math.cos(xRad / 2);
const sx = Math.sin(xRad / 2);
const cy = Math.cos(yRad / 2);
const sy = Math.sin(yRad / 2);
const cz = Math.cos(zRad / 2);
const sz = Math.sin(zRad / 2);
return {
x: sx * cy * cz + cx * sy * sz,
y: cx * sy * cz - sx * cy * sz,
z: cx * cy * sz + sx * sy * cz,
w: cx * cy * cz - sx * sy * sz
};
}
function multiplyQuaternions(q1: Quaternion, q2: Quaternion): Quaternion {
return {
x: q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y,
y: q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x,
z: q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w,
w: q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
};
}
function createMeshResource(): Promise<MeshResource> {
const renderContext: RenderContext | null = Scene.getDefaultRenderContext();
if (!renderContext) {
return Promise.reject(new Error("RenderContext is null"));
}
const renderResourceFactory: RenderResourceFactory = renderContext.getRenderResourceFactory();
const geometry = new CustomGeometry();
geometry.vertices = [
// 前面 (z=0.5)
{ x: -0.5, y: -0.5, z: 0.5 },
{ x: 0.5, y: -0.5, z: 0.5 },
{ x: 0.5, y: 0.5, z: 0.5 },
{ x: -0.5, y: 0.5, z: 0.5 },
// 后面 (z=-0.5)
{ x: -0.5, y: -0.5, z: -0.5 },
{ x: 0.5, y: -0.5, z: -0.5 },
{ x: 0.5, y: 0.5, z: -0.5 },
{ x: -0.5, y: 0.5, z: -0.5 },
// 右面 (x=0.5)
{ x: 0.5, y: -0.5, z: 0.5 },
{ x: 0.5, y: -0.5, z: -0.5 },
{ x: 0.5, y: 0.5, z: -0.5 },
{ x: 0.5, y: 0.5, z: 0.5 },
// 左面 (x=-0.5)
{ x: -0.5, y: -0.5, z: 0.5 },
{ x: -0.5, y: -0.5, z: -0.5 },
{ x: -0.5, y: 0.5, z: -0.5 },
{ x: -0.5, y: 0.5, z: 0.5 },
// 上面 (y=0.5)
{ x: -0.5, y: 0.5, z: 0.5 },
{ x: 0.5, y: 0.5, z: 0.5 },
{ x: 0.5, y: 0.5, z: -0.5 },
{ x: -0.5, y: 0.5, z: -0.5 },
// 下面 (y=-0.5)
{ x: -0.5, y: -0.5, z: 0.5 },
{ x: 0.5, y: -0.5, z: 0.5 },
{ x: 0.5, y: -0.5, z: -0.5 },
{ x: -0.5, y: -0.5, z: -0.5 }
];
geometry.indices = [
0, 1, 2, 2, 3, 0, // 前面
4, 5, 6, 6, 7, 4, // 后面
8, 9, 10, 10, 11, 8, // 右面
12, 13, 14, 14, 15, 12, // 左面
16, 17, 18, 18, 19, 16, // 上面
20, 21, 22, 22, 23, 20 // 下面
];
geometry.topology = PrimitiveTopology.TRIANGLE_LIST;
geometry.normals = [
// 前面:法线(0,0,1)
{ x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: 1 },
// 后面:法线(0,0,-1)
{ x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: -1 },
// 右面:法线(1,0,0)
{ x: 1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 },
// 左面:法线(-1,0,0)
{ x: -1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 },
// 上面:法线(0,1,0)
{ x: 0, y: 1, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: 1, z: 0 }, { x: 0, y: 1, z: 0 },
// 下面:法线(0,-1,0)
{ x: 0, y: -1, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: -1, z: 0 }, { x: 0, y: -1, z: 0 }
];
geometry.uvs = [
{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 1 },
{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 1 },
{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 1 },
{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 1 },
{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 1 },
{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 1 }
];
const sceneResourceParameter: SceneResourceParameters = { name: "cubeMesh" };
return renderResourceFactory.createMesh(sceneResourceParameter, geometry);
}